This document is the summary of the R for Data Analysis workshop.

All correspondence related to this document should be addressed to:

Omid Ghasemi (Macquarie University, Sydney, NSW, 2109, AUSTRALIA)

Email:

1 Introduction to R

1.1 Basics and Variables

R can be used as a calculator. For mathematical purposes, be careful of the order in which R executes the commands.

10 + 10
## [1] 20
4 ^ 2
## [1] 16
(250 / 500) * 100
## [1] 50

R is a bit flexible with spacing (but no spacing in the name of variables and words)

10+10
## [1] 20
10                 +           10
## [1] 20

R can sometimes tell that you’re not finished yet

10 +

How to create a variable? Variable assignment using <- and =. Note that R is case sensitive for everything

pay <- 250

month = 12

pay * month
## [1] 3000
salary <- pay * month

Few points in naming variables and vectors: use short, informative words, keep same method (e.g., you can use capital letters but it is not recommended, use only _ or . ).

1.2 Function

Function is a set of statements combined together to perform a specific task. When we use a block of code repeatedly, we can convert it to a function. To write a function, first, you need to define it:

my_multiplier <- function(a,b){
  result = a * b
  return (result)
}

This code do nothing. To get a result, you need to call it:

my_multiplier (a=2, b=4)
## [1] 8
# or: my_multiplier (2, 4)

We can set a default value for our arguments:

my_multiplier2 <- function(a,b=4){
  result = a * b
  return (result)
}

my_multiplier2 (a=2)
## [1] 8
# or: my_multiplier (2)
# or: my_multiplier (2, 6)

Fortunately, you do not need to write everything from scratch. R has lots of built-in functions that you can use:

round(54.6787)
## [1] 55
round(54.5787, digits = 2)
## [1] 54.58

Use ? before the function name to get some help. For example, ?round. You will see many functions in the rest of the workshop.

1.3 Data Types

function class() is used to show what is the type of a variable.

  1. Logical: TRUE, FALSE can be abbreviated as T, F. They has to be capital, ‘true’ is not a logical data:
class(TRUE)
## [1] "logical"
class(F)
## [1] "logical"
  1. Numeric: all numbers e.g. 5, 10.5, 11,37; a special type of numeric is “integer” which is numbers without decimal. Integers are always numeric, but numeric is not always integer:
class(2)
## [1] "numeric"
class(13.46)
## [1] "numeric"
  1. Character: text for example, “I love R” or “4” or “4.5”:
class("ha ha ha ha")
## [1] "character"
class("56.6")
## [1] "character"
class("TRUE")
## [1] "character"

Can we change the type of data in a variable? Yes, you need to use the function as.---()

as.numeric(TRUE)
## [1] 1
as.character(4)
## [1] "4"
as.numeric("4.5")
## [1] 4.5
as.numeric("Hello")
## Warning: NAs introduced by coercion
## [1] NA

1.4 Data Structures

1.4.1 Vector

When there are more than one number or letter stored. Use the combine function c() for that.

sale <- c(1, 2, 3,4, 5, 6, 7, 8, 9, 10) # also sale <- c(1:10)

sale <- c(1:10)

sale * sale
##  [1]   1   4   9  16  25  36  49  64  81 100

Subsetting a vector:

days <- c("Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday")

days[2]
## [1] "Sunday"
days[-2]
## [1] "Saturday"  "Monday"    "Tuesday"   "Wednesday" "Thursday"  "Friday"
days[c(2, 3, 4)]
## [1] "Sunday"  "Monday"  "Tuesday"
  • Exercise: Create a vector named my_vector with numbers from 0 to 1000 in it and calculate mean, median, sd, min, max, and sum of that vector:
my_vector <- (0:1000)

mean(my_vector)
## [1] 500
median(my_vector)
## [1] 500
min(my_vector)
## [1] 0
range(my_vector)
## [1]    0 1000
class(my_vector)
## [1] "integer"
sum(my_vector)
## [1] 500500
sd(my_vector)
## [1] 289.1081

1.4.2 List

List allows you to gather a variety of objects under one name (that is, the name of the list) in an ordered way. These objects can be matrices, vectors, data frames, even other list.

my_list = list(sale, 1, 3, 4:7, "HELLO", "hello", FALSE)
my_list
## [[1]]
##  [1]  1  2  3  4  5  6  7  8  9 10
## 
## [[2]]
## [1] 1
## 
## [[3]]
## [1] 3
## 
## [[4]]
## [1] 4 5 6 7
## 
## [[5]]
## [1] "HELLO"
## 
## [[6]]
## [1] "hello"
## 
## [[7]]
## [1] FALSE

1.4.3 Factor

Factors store the vector along with the distinct values of the elements in the vector as labels. The labels are always character irrespective of whether it is numeric or character. For example, variable gender with “male” and “female” entries:

gender <- c("male", "male", "male", " female", "female", "female")
gender <- factor(gender)

R now treats gender as a nominal (categorical) variable: 1=female, 2=male internally (alphabetically).

summary(gender)
##  female  female    male 
##       1       2       3
  • Question: why when we ran the above function i.e. summary(), it showed three and not two levels of the data? Hint: run ‘gender’.
gender
## [1] male    male    male     female female  female 
## Levels:  female female male

So, be careful of spaces!

  • Exercise: Create a gender factor with 30 male and 40 females (Hint: use the rep() function):
gender <- c(rep("male",30), rep("female", 40))
gender <- factor(gender)
gender
##  [1] male   male   male   male   male   male   male   male   male   male  
## [11] male   male   male   male   male   male   male   male   male   male  
## [21] male   male   male   male   male   male   male   male   male   male  
## [31] female female female female female female female female female female
## [41] female female female female female female female female female female
## [51] female female female female female female female female female female
## [61] female female female female female female female female female female
## Levels: female male

There are two types of categorical variables: nominal and ordinal. How to create ordered factors (when the variable is nominal and values can be ordered)? We should add two additional arguments to the factor() function: ordered = TRUE, and levels = c("level1", "level2"). For example, we have a vector that shows participants’ education level.

edu<-c(3,2,3,4,1,2,2,3,4)

education<-factor(edu, ordered = TRUE)
levels(education) <- c("Primary school","high school","College","Uni graduated")
education
## [1] College        high school    College        Uni graduated  Primary school
## [6] high school    high school    College        Uni graduated 
## Levels: Primary school < high school < College < Uni graduated
  • Exercise: We have a factor with patient and control values. Here, the first level is control and the second level is patient. Change the order of levels, so patient would be the first level:
health_status <- factor(c(rep('patient',5),rep('control',5)))
health_status
##  [1] patient patient patient patient patient control control control control
## [10] control
## Levels: control patient
health_status_reordered <- factor(health_status, levels = c('patient','control'))
health_status_reordered
##  [1] patient patient patient patient patient control control control control
## [10] control
## Levels: patient control

Finally, can you relabel both levels to uppercase characters? (Hint: check ?factor)

health_status_relabeled <- factor(health_status, levels = c('patient','control'), labels = c('Patient','Control'))
health_status_relabeled
##  [1] Patient Patient Patient Patient Patient Control Control Control Control
## [10] Control
## Levels: Patient Control

1.4.4 Matrices

All columns in a matrix must have the same mode(numeric, character, etc.) and the same length. It can be created using a vector input to the matrix function.

my_matrix = matrix(c(1,2,3,4,5,6,7,8,9), nrow = 3, ncol = 3)

my_matrix
##      [,1] [,2] [,3]
## [1,]    1    4    7
## [2,]    2    5    8
## [3,]    3    6    9

1.4.5 Data frames

Data frames can hold numeric, character or logical values. Within a column all elements have the same data type, but different columns can be of different data type. Let’s create a dataframe:

id <- 1:200
group <- c(rep("Psychotherapy", 100), rep("Medication", 100))
response <- c(rnorm(100, mean = 30, sd = 5),
             rnorm(100, mean = 25, sd = 5))

my_dataframe <-data.frame(Patient = id,
                          Treatment = group,
                          Response = response)

We also could have done the below

my_dataframe <-data.frame(Patient = c(1:200),
                          Treatment = c(rep("Psychotherapy", 100), rep("Medication", 100)),
                          Response = c(rnorm(100, mean = 30, sd = 5),
                                       rnorm(100, mean = 25, sd = 5)))

In large data sets, the function head() enables you to show the first observations of a data frames. Similarly, the function tail() prints out the last observations in your data set.

head(my_dataframe) 
tail(my_dataframe)
Patient Treatment Response
1 1 Psychotherapy 35.59862
2 2 Psychotherapy 36.62962
3 3 Psychotherapy 29.01446
4 4 Psychotherapy 22.71264
5 5 Psychotherapy 33.70909
6 6 Psychotherapy 35.58150
Patient Treatment Response
195 195 Medication 19.73908
196 196 Medication 23.20115
197 197 Medication 24.25496
198 198 Medication 31.60629
199 199 Medication 14.19786
200 200 Medication 33.20383

Similar to vectors and matrices, brackets [] are used to selects data from rows and columns in data.frames:

my_dataframe[35, 3]
## [1] 39.75112
  • Exercise: How can we get all columns, but only for the first 10 participants?
my_dataframe[1:10, ]
Patient Treatment Response
1 Psychotherapy 35.59862
2 Psychotherapy 36.62962
3 Psychotherapy 29.01446
4 Psychotherapy 22.71264
5 Psychotherapy 33.70909
6 Psychotherapy 35.58150
7 Psychotherapy 26.91184
8 Psychotherapy 24.35061
9 Psychotherapy 30.09706
10 Psychotherapy 28.20006

How to get only the Response column for all participants?

my_dataframe[ , 3]
##   [1] 35.59862 36.62962 29.01446 22.71264 33.70909 35.58150 26.91184 24.35061
##   [9] 30.09706 28.20006 32.09037 25.15059 25.23804 35.73875 42.61423 32.24317
##  [17] 24.60450 33.02065 36.51803 33.96189 43.34802 31.97126 31.66812 28.79656
##  [25] 25.97128 33.38626 30.57732 23.27546 19.68985 28.17951 29.98973 27.16092
##  [33] 31.99661 26.27210 39.75112 27.47717 16.54230 21.75016 33.36571 36.44900
##  [41] 30.70574 29.09672 29.43279 22.77227 20.96891 36.73037 28.83073 31.67700
##  [49] 34.54895 28.85963 27.09316 36.16230 24.35933 23.88968 34.71001 24.40981
##  [57] 32.97373 33.39629 18.96927 35.96424 27.82566 21.47080 30.61677 32.20519
##  [65] 35.46101 26.53701 39.78074 30.75453 20.34907 30.20975 36.76597 32.91871
##  [73] 25.03069 35.65879 27.68528 36.40671 27.88243 28.90775 25.83936 41.69814
##  [81] 44.44514 27.74542 28.13034 23.14361 20.74980 31.84590 36.95674 32.19458
##  [89] 27.70576 34.45078 31.68835 29.76735 30.29011 30.04594 28.00025 34.60662
##  [97] 25.64952 31.98999 29.15195 28.50349 24.16872 19.86797 19.86773 28.39305
## [105] 22.58449 21.74178 30.88721 21.84749 28.48663 22.40085 35.49097 28.02832
## [113] 31.35924 23.76777 21.67946 14.10546 22.86193 27.90598 24.61302 23.26970
## [121] 15.32896 30.49925 21.78486 23.99288 25.47913 34.21774 24.12370 26.81657
## [129] 29.65434 18.07262 28.98266 25.36519 31.33496 15.23519 24.89588 17.40844
## [137] 35.31352 19.03414 30.53285 20.96974 36.13117 25.31783 24.70286 24.30349
## [145] 23.77750 25.06979 16.42690 29.35791 34.99587 16.98754 26.94997 28.67525
## [153] 22.93114 18.99034 13.04770 27.22615 22.70794 27.14391 15.72608 28.32175
## [161] 17.88373 31.00352 20.02655 31.80458 29.67562 23.72706 30.19105 25.62265
## [169] 15.54247 19.61728 25.53547 28.17935 30.10152 35.65953 16.25308 19.49600
## [177] 23.54306 22.12626 22.47110 27.36396 28.10063 27.52071 27.52407 27.88606
## [185] 16.75499 29.03299 24.35035 30.20982 27.80097 18.65039 24.78246 24.88516
## [193] 26.85609 33.23869 19.73908 23.20115 24.25496 31.60629 14.19786 33.20383

Another easier way for selecting particular items is using their names that is more helpful than number of the rows in large data sets:

my_dataframe[ , "Response"]
# OR:
my_dataframe$Response

So far, we created dataframes using data.frame function from the base R. However, a better way to create dataframes is to use the tibble function from tidyverse (see here).

2 Data Cleaning

Now, suppose we ran an experiment with 141 depressed patients. Participants were randomly assigned into two treatment groups: CBT or Psychodynamic psychotherapy. We measured self-report depression scores at 5 different stages of treatment:

  • Stage 1: Before starting any treatment. It is our base stage (pre-test)
  • Stage 2: After 5 sessions of psychotherapy (post-test1)
  • Stage 3: After 10 sessions of psychotherapy (post-test2)
  • Stage 4: At the end of the treatment (post-test3)
  • Stage 5: Three months after the treatment (post-test4)

let’s read and check the uncleaned data. But, first thing first. let’s install and then load the tidyvese package. We also need some other packages:

# Install it
install.packages("tidyverse")

# And then load it
library(tidyverse)

# Load other packages that you have already installed
library(here)
library(janitor)
library(broom)
library(afex)
library(emmeans)
library(knitr)
library(kableExtra)
library(ggsci)
library(patchwork)
library(skimr)
# install.packages("devtools")
# devtools::install_github("easystats/correlation")
library("correlation")
options(scipen=999) # turn off scientific notations
options(contrasts = c('contr.sum','contr.poly')) # set the contrast sum globally 
options(knitr.kable.NA = '')
# read the raw data
raw_data <- read_csv(here("raw_data","raw_data_exp1.csv"))
head(raw_data)
progress subject response_id consent_form age gender stage1_cbt stage2_cbt stage3_cbt stage4_cbt stage5_cbt stage1_dynamic stage2_dynamic stage3_dynamic stage4_dynamic stage5_dynamic anxiety1 anxiety2 anxiety3 anxiety4 anxiety5 anxiety6 anxiety7 anxiety8 group sleep_quality life_satisfaction
100 subj1 R_1f298znjmVzcOjp I consent 18 Female 90 31 33 47 50 5 5 5 5 5 6 5 3 Psychodynamic 9 9
100 subj2 R_tL0A9P33Gi18I0N I consent 18 Male 78 46 46 11 13 6 6 6 5 6 6 5 6 CBT 9 10
100 subj3 R_1LNyJhCKxTAAMOW I consent 19 Female 68 51 24 41 24 6 5 4 5 5 6 5 6 CBT 10 8
100 subj4 R_3enxzUsEYgs5r1a I consent 27 Female 100 21 11 6 31 6 6 6 1 6 6 6 1 Psychodynamic 8 7
100 subj5 R_2Qzl2096a4KNE29 I consent 19 Male 30 28 16 6 6 6 6 5 5 6 6 6 6 CBT 11 11
100 subj6 R_esb71WOTQySjusF I consent 20 Female 79 1 57 46 57 6 6 6 5 6 6 6 6 Psychodynamic 10 10
  • Exercise: There is a dataset in the cleaned_data folder named unicef_u5mr.csv. Read the dataset using read_csv and here.
unicef_data <- read_csv(here("cleaned_data","unicef_u5mr.csv"))

In order to clean the data, we use tidyverse which is a collection of packages to work with data. One of the tidyverse packages that we use regularly is dplyr which includes several functions:

  • mutate() adds new variables or change existing ones.
  • select() pick variables (columns) based on their names.
  • filter() picks cases (rows) based on their values.
  • summarise() gives a single single summary of the data (e.g., mean, counts, etc.)
  • arrange() changes the ordering of the rows.
  • group_by() divides your dataframe into grouped dataframes and allow you to do each of the above operations (except for arrange) on every one of them separately.

2.1 Select

Pick subject, age, and gender columns:

selected_data <- select(raw_data, subject, age, gender)

2.2 Filter

Now, do the following tasks: pick all the male participants, pick all the male participants or those greater than 25 years old, and finally pick all male participants and those greater than 25 years old:

# filter all males
fil_male <- filter(raw_data, gender == "Male")
# filter males and older than 25
fil_male_and_g25 <- filter(raw_data, gender == "Male" & age > 25 )
# filter males or older than 25
fil_male_or_g25 <- filter(raw_data, gender == "Male" | age > 25 )

2.3 Arrange

Arrange (order) your dataframe based on the age, once in an ascending order (youngers first) and once based on descending order (olders first):

# order participants based on their age
arranged_data <- arrange(raw_data, age)
# order participants based on their age (descendeing)
arranged_descending <- arrange(raw_data, desc(age))

2.4 Mutate

Create a column to show if the participant has finished the task or not:

mutated_data <- mutate (raw_data, finished= case_when(progress==100~ "Yes",T~ "No"))

2.5 Summarise

Summarize participants age and sd:

summarise(raw_data, mean= mean(age, na.rm=T),
          sd= sd (age, na.rm=T))
mean sd
21.27273 6.635655

2.6 Pipe Operators

A new function: pipe operators %>% pipes a value into the next function:

raw_data %>% 
  summarise(., mean= mean(age, na.rm=T),
            sd= sd (age, na.rm=T))
mean sd
21.27273 6.635655
raw_data %>% 
  summarise(mean= mean(age, na.rm=T),
            sd= sd (age, na.rm=T))
mean sd
21.27273 6.635655

Calculate the age mean of younger than 25 participants only:

raw_data %>% 
  filter (age < 25) %>%
  summarise(mean= mean(age, na.rm=T),
            sd= sd (age, na.rm=T))
mean sd
19.1913 1.515393

2.7 Group By

Calculate the age mean of younger than 25 participants for each gender separately:

raw_data %>% 
  filter (age < 25) %>%
  group_by(gender) %>%
  summarise(mean= mean(age, na.rm=T),
            sd= sd (age, na.rm=T)) %>%
  ungroup ()
gender mean sd
Female 19.21053 1.556693
Male 19.10000 1.333772
  • Exercise: Create a column to show if participant is older than 23 or not and then calculate sleep quality (sleep_quality) mean for each group separately:
raw_data %>%
  mutate(age_group = case_when(age > 23 ~ "greater than 23", T~ "younger than 23")) %>%
  group_by(age_group) %>%
  summarise(sleep_quality = mean(sleep_quality, na.rm=T))
age_group sleep_quality
greater than 23 9.000000
younger than 23 8.107438
  • Exercise: Add the anxiety total score (sum) to the dataframe and then convert subject column to factor:
anxiety_data <- raw_data %>%
  mutate(anxiety_total= anxiety1+anxiety2+anxiety3+anxiety4+anxiety5+anxiety6+anxiety7+anxiety8) %>%
  mutate(subject= factor(subject))

2.8 Pivoting

Next, we want to pivot our data to switch between long and wide format:

# Make you data long
long_data <- raw_data %>%
  select(subject, stage1_cbt:stage5_cbt,stage1_dynamic:stage5_dynamic) %>%
  pivot_longer(cols = c(stage1_cbt:stage5_dynamic), names_to = 'stage', values_to = 'depression_score')

# Make you data wide
wide_data <- long_data %>%
  pivot_wider(names_from = stage, values_from= depression_score)
  • Exercise: Convert the UNICEF dataset to long and wide formats:
unicef_data <- read_csv(here("cleaned_data","unicef_u5mr.csv"))

library(janitor)
unicef_data_cleaned <- unicef_data %>%
  clean_names()

unicef_long_data <- unicef_data_cleaned %>% pivot_longer(cols = c(u5mr_1950:u5mr_2015), names_to = 'year', values_to = 'u5mr')
unicef_wideg_data <- unicef_long_data %>% pivot_wider(names_from = 'year', values_from = 'u5mr')

Note: The codes for the previous exercise were taken from this blog post written by Simon Ejdemyr.

Now, let’s do some cleaning using dplyr, tidyr and other tidyverse libraries:

cleaned_data <- raw_data %>% 
  filter(progress == 100) %>% # filter out unfinished participants
  select(-consent_form) %>% #remove some useless columns
  # create a total score for our questionnaire
  mutate(anxiety_total= anxiety1+anxiety2+anxiety3+anxiety4+anxiety5+anxiety6+anxiety7+anxiety8) %>%
  select(-anxiety1:-anxiety8) %>%
  # make our dataframe long
  pivot_longer(cols = c(stage1_cbt:stage5_cbt,stage1_dynamic:stage5_dynamic),names_to = 'stage',values_to = 'depression_score') %>% 
  #pivot_wider(names_from = stage, values_from= depression_score) # this code change our dataframe back to wide
  filter(!is.na(depression_score)) %>% #remove rows with depression_score == NA
  mutate(stage= gsub("_.*", "", stage)) %>%
  select (subject, age, gender, group, stage, depression_score, anxiety_total, sleep_quality, life_satisfaction)
subject age gender group stage depression_score anxiety_total sleep_quality life_satisfaction
subj1 18 Female Psychodynamic stage1 90 39 9 9
subj1 18 Female Psychodynamic stage2 31 39 9 9
subj1 18 Female Psychodynamic stage3 33 39 9 9
subj1 18 Female Psychodynamic stage4 47 39 9 9
subj1 18 Female Psychodynamic stage5 50 39 9 9
subj2 18 Male CBT stage1 78 46 9 10

Ok, now the data is clean and tidy which means:

  1. Each variable forms a column.
  2. Each observation forms a row.
  3. Each type of observational unit forms a table (Wickham, 2014).

Check the dataframe and all the data types:

str(cleaned_data)
## tibble [655 × 9] (S3: tbl_df/tbl/data.frame)
##  $ subject          : chr [1:655] "subj1" "subj1" "subj1" "subj1" ...
##  $ age              : num [1:655] 18 18 18 18 18 18 18 18 18 18 ...
##  $ gender           : chr [1:655] "Female" "Female" "Female" "Female" ...
##  $ group            : chr [1:655] "Psychodynamic" "Psychodynamic" "Psychodynamic" "Psychodynamic" ...
##  $ stage            : chr [1:655] "stage1" "stage2" "stage3" "stage4" ...
##  $ depression_score : num [1:655] 90 31 33 47 50 78 46 46 11 13 ...
##  $ anxiety_total    : num [1:655] 39 39 39 39 39 46 46 46 46 46 ...
##  $ sleep_quality    : num [1:655] 9 9 9 9 9 9 9 9 9 9 ...
##  $ life_satisfaction: num [1:655] 9 9 9 9 9 10 10 10 10 10 ...

Finally, we save our data to the cleaned_data folder.

write_csv(cleaned_data, here("cleaned_data","cleaned_data_exp1.csv"))

3 Data Visualization

Before starting the ggplot, let’s try a visualization using a function from the Base R the plot() function shows the association of each variable against the other one in a data handy for data with few number of variables to see if there are any patterns

exam_data<- read_csv(here::here("cleaned_data", "exam_data.csv"))

plot(x = exam_data$Anxiety, y = exam_data$Exam)

The code also works without writing x and y, however, writing them is strongly recommended

plot(exam_data$Anxiety, exam_data$Exam)

ggplot, the gg in ggplot stands for grammar of graphics. Grammar of graphics basically says any graphical representation of data, can be produced by a series of layers. You can think of a layer as a plastic transparency. Lets draw the same plot using ggplot. Always, mention the data we are going to work with.

ggplot(data = exam_data, aes(x = Exam, y = Anxiety))

  • aes: aes which stands for aesthetics is a relationship between a variable in your dataset and an aspect of the plot that is going to visually convey the information to the reader

  • Visual elements are known as geoms (short for ‘geometric objects’) in ggplot 2. When we define a layer, we have to tell R what geom we want displayed on that layer (do we want a bar, line dot, etc.?)

ggplot(data = exam_data, aes(x = Exam, y = Anxiety))+ geom_point()

So, lets try some of them here like shape and size. Be careful with the + sign, if you clink enter for the next part of the code, the + sign should not go to the next line

ggplot(data = exam_data, aes(x = Exam, y = Anxiety))+
  geom_point(size = 2, shape = 8)

The current plot is not very informative about the patterns for each gender.

ggplot(data = exam_data, aes(x = Exam, y = Anxiety, color = Gender))+
  geom_point(size = 2, shape = 10)

ggplot(data = exam_data, aes(x = Exam, y = Anxiety, color = Gender, shape = Gender))+
  geom_point(size = 2, shape = 10)

Question: why the above code doesn’t make any change?

ggplot(data = exam_data, aes(x = Exam, y = Anxiety, color = Gender, shape = Gender))+
  geom_point(size = 2)

Can assign the first layer to a variable to reduce the length of codes for next layers.

My_graph <- ggplot(data = exam_data, aes(x = Exam, y = Anxiety))

My_graph + geom_point()

lets add a line to the current graph

My_graph + geom_point() + geom_smooth()

Aesthetics can be set for all layers of the plot (i.e., defined in the plot as a whole) or can be set individually for each geom in a plot.

My_graph + geom_point(aes(color = Gender)) + geom_smooth()

My_graph + geom_point(aes(color = Gender)) + geom_smooth(aes(color = Gender))

The shaded area around the line is the 95% confidence interval around the line. We can switch this off by adding se = F (which is short for ‘standard error = False’)

My_graph + geom_point() + geom_smooth(se = F)

What if we want our line to be a direct line?

My_graph + geom_point() + geom_smooth(se = F, method = lm)

How to change the labels of x and y axes?

My_graph + geom_point() + geom_smooth(se = F, method = lm) +
  labs(x = "Exam scores %", y = "Anxiety scores")

Histograms are used to show distributions of variables while bar charts are used to compare variables. Histograms plot quantitative data with ranges of the data grouped into bins or intervals while bar charts plot categorical data.

#ggplot(data = exam_data, aes(x = Anxiety, y = Exam )) + geom_histogram()
# the code above gives an error as geom_histogram can only have x or y axis in its aes()

ggplot(data = exam_data, aes(x = Anxiety)) + geom_histogram()

ggplot(data = exam_data, aes(y = Anxiety)) + geom_histogram()

ggplot(data = exam_data, aes(x = Anxiety)) + geom_histogram(bins = 31)

ggplot(data = exam_data, aes(x = Anxiety)) + geom_histogram(bins = 31, fill = "green")

ggplot(data = exam_data, aes(x = Anxiety)) + geom_histogram(bins = 31, fill = "green", col = "red")

Let’s stop using the My_graph variable and write the whole code from the start again for a bar chart

ggplot(data = exam_data, aes(x = Sleep_quality))+
  geom_bar()

Because we want to plot a summary of the data (the mean) rather than the raw scores themselves, we have to use a stat.

ggplot(data = exam_data, aes(x = Sleep_quality, y = Exam, fill = Gender))+
  geom_bar(stat = "summary", fun = "mean")

ggplot(data = exam_data, aes(x = Sleep_quality, y = Exam, fill = Gender))+
  geom_bar(stat = "summary", fun = "mean", position = "dodge")

The other way to get the same plot that the code above gives, is using the stat_summary function that takes the following general form: stat_summary(function = x, geom = y)

ggplot(data = exam_data, aes(x = Sleep_quality, y = Exam, fill = Gender))+
  stat_summary(fun = mean, geom = "bar", position = "dodge")

How to combine multiple plots? How to combine multiple plots? We can use the patchwork package. A nice tutorial on using this package can be found here

p1 = My_graph + geom_point(aes(color = Gender)) + geom_smooth()

p2 = ggplot(data = exam_data, aes(x = Anxiety)) + geom_histogram(bins = 31)

p3 = ggplot(data = exam_data, aes(x = Sleep_quality, y = Exam, fill = Gender))+
  stat_summary(fun = mean, geom = "bar", position = "dodge")

p4 = My_graph + geom_point() + geom_smooth(se = F, method = lm) +
  labs(x = "Exam scores %", y = "Anxiety scores")

combined = p1 + p2+ p3 + p4 + plot_layout(nrow = 4, byrow = F)

combined

p1 | p2 / p3 / p4

p1 | p2 / (p3 / p4)

ggsave() function, which is a versatile exporting function that can export as PostScript (.eps/.ps), tex (pictex), pdf, jpeg, tiff, png, bmp, svg and wmf (in Windows only). In its basic form, the structure of the function is very simple: ggsave(filename)

ggsave(combined, filename = here("outputs", "combined.png"), dpi=300)

Now that we learned the basics of ggplot, let’s draw some plot for our experiment data. First, we need to create a dataset with aggregated depression_score scores over group and stage. We will use this dataset for line and bar graphs.

library(ggsci)

data_exp1_orig <- read_csv(here("cleaned_data","cleaned_data_exp1.csv"))

data_exp1 <- data_exp1_orig%>% 
  #mutate_if(is.character, factor) %>%
  mutate(subject= factor(subject), # convert all characters to factor
         group = factor(group),
         stage = factor(stage))


aggregated_data_exp1 <- data_exp1 %>%
  group_by(stage, group) %>%
  mutate(depression_score = mean(depression_score)) %>%
  ungroup()


barplot_exp1 <- aggregated_data_exp1 %>%
  ggplot(aes(x=stage, y= depression_score, fill=group)) +
  geom_bar(stat = "identity", position= "dodge")+
  labs (x= '', y= "Depression Score") + 
  theme_bw() + 
  scale_fill_jama() 

#ggsave(barplot_exp1, filename = here("outputs","barplot_exp1.png"), dpi=300)


barplot_facet_exp1 <- aggregated_data_exp1 %>%
  ggplot(aes(x=group, y= depression_score, fill=stage)) +
  geom_bar(stat = "identity", position= "dodge")+
  labs (x= '', y= "Depression Score") + 
  theme_bw() + 
  theme(legend.position = "none",
        axis.text=element_text(size=11),
        axis.title = element_text(size = 12)) +
  facet_wrap(~stage, nrow = 1)+
  scale_fill_jco() 

#ggsave(barplot_facet_exp1, filename = here("outputs","barplot_facet_exp1.png"), dpi=300)


lineplot_exp1 <- aggregated_data_exp1 %>%
  ggplot(aes(x=factor(stage), y= depression_score, group= group, color= group)) +
  geom_line(aes(linetype= group)) +
  geom_point(size= 5)+
  labs (x= '', y= "Depression Score") + 
  theme_classic() +
  theme(legend.position = "bottom",
        axis.text=element_text(size=11),
        axis.title = element_text(size = 12)) +
  scale_color_nejm() 

#ggsave(lineplot_exp1, filename = here("outputs","lineplot_exp1.png"), dpi=300)


violinplot_exp1 <- data_exp1 %>%
  ggplot(aes(x=factor(stage), y= depression_score, fill= group)) +
  geom_violin()+
  labs (x= '', y= "Depression Score") + 
  theme_bw() + 
  theme(legend.position = "bottom",
        axis.text=element_text(size=11)) +
  scale_fill_d3() 

#ggsave(violinplot_exp1, filename = here("outputs","violinplot_exp1.png"), dpi=300)


boxplot_exp1 <- data_exp1 %>%
  ggplot(aes(x=factor(stage), y= depression_score, fill= group)) +
  geom_boxplot()+
  #geom_point(position = position_dodge(width=0.75), alpha= .5)+
  labs (x= '', y= "Depression Score") + 
  theme_bw() + 
  theme(legend.position = "bottom",
        axis.text=element_text(size=11)) +
  scale_fill_simpsons() 

#ggsave(boxplot_exp1, filename = here("outputs","boxplot_exp1.png"), dpi=300)


boxplot_facet_exp1 <- data_exp1 %>%
  ggplot(aes(x=factor(stage), y= depression_score, fill= group)) +
  geom_boxplot()+
  labs (x= '', y= "Depression Score") + 
  theme_bw() + 
  theme(legend.position = "bottom",
        axis.text=element_text(size=11),
        axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
  facet_wrap(~group)+
  scale_color_simpsons() 

#ggsave(boxplot_facet_exp1, filename = here("outputs","boxplot_facet_exp1.png"), dpi=300)

Let’s combine our plots:

combined_plot_exp1 <- barplot_facet_exp1 / (lineplot_exp1+violinplot_exp1+boxplot_exp1)
combined_plot_exp1

And here, we save our plots to the outputs folder.

ggsave(combined_plot_exp1, filename = here("outputs","combined_plot_exp1.png"), dpi=300, width = 12)

4 Descriptive Statistics

Now, let’s do some descriptive statistics. Now, we can open a new script called data_analysis.r and read some datasets. Then we use skimr package to describe our data.

narcissism_data <- read_csv(here("cleaned_data","narcissism_data.csv"))
narcissism_data %>% skimr::skim()
Data summary
Name Piped data
Number of rows 131
Number of columns 5
_______________________
Column type frequency:
character 1
numeric 4
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
subject 0 1 5 7 0 131 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
psychopathy 0 1 8.78 2.27 0 8.0 10 10 11 ▁▁▁▂▇
self_esteem 0 1 8.45 1.68 4 8.0 8 9 12 ▁▅▇▆▃
narcissism 0 1 38.20 6.15 19 33.5 39 43 48 ▁▂▇▇▆
mental_health 0 1 3.19 1.04 1 3.0 4 4 4 ▂▂▁▃▇
  • Exercise: Open the dataset called treatment_data.csv and do a descriptive analysis:
treatment_data <- read_csv(here("cleaned_data","treatment_data.csv"))
treatment_data %>% skimr::skim()
Data summary
Name Piped data
Number of rows 131
Number of columns 7
_______________________
Column type frequency:
character 3
numeric 4
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
subject 0 1 5 7 0 131 0
gender 0 1 4 6 0 2 0
treatment 0 1 3 13 0 2 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
age 0 1 21.15 6.52 16 18.0 19 20.0 63 ▇▁▁▁▁
anxiety 0 1 62.35 24.51 0 40.0 69 81.0 100 ▂▆▃▇▆
depression 0 1 52.50 22.12 0 34.5 51 71.0 100 ▂▇▇▆▃
life_satisfaction 0 1 41.02 23.93 0 21.0 39 56.5 100 ▅▇▅▃▂
  • Exercise: Do the same thing for the memory_data.csv.
memory_data <- read_csv(here("cleaned_data","memory_data.csv"))
memory_data %>% group_by(time) %>%
  skimr::skim()
Data summary
Name Piped data
Number of rows 262
Number of columns 5
_______________________
Column type frequency:
character 2
numeric 2
________________________
Group variables time

Variable type: character

skim_variable time n_missing complete_rate min max empty n_unique whitespace
subject post_test_memory 0 1 5 7 0 131 0
subject pre_test_memory 0 1 5 7 0 131 0
gender post_test_memory 0 1 4 6 0 2 0
gender pre_test_memory 0 1 4 6 0 2 0

Variable type: numeric

skim_variable time n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
age post_test_memory 0 1 21.15 6.52 16 18.0 19 20.0 63 ▇▁▁▁▁
age pre_test_memory 0 1 21.15 6.52 16 18.0 19 20.0 63 ▇▁▁▁▁
memory_score post_test_memory 0 1 52.50 22.12 0 34.5 51 71.0 100 ▂▇▇▆▃
memory_score pre_test_memory 0 1 41.02 23.93 0 21.0 39 56.5 100 ▅▇▅▃▂
  • Exercise: For this exercise, we use a dataset of one of my own studies. In this study, we asked participants to guess the physical brightness of some reasoning arguments and then we gave a cognitive ability test. (See the original study here). Open ghasemi_brightness_exp4.csv file and answer to the following questions:
  1. How many participants did we test in total?
  2. Find out how many male and female we tested.
  3. Calculate mean and sd for age and cognitive ability (cog_ability).
ghasemi_data <- read_csv(here("cleaned_data","ghasemi_brightness_exp4.csv"))

ghasemi_data %>% summarise(n = n_distinct(participant)) # number of participants:200
n
200
ghasemi_data %>% group_by (participant) %>% filter (row_number()==1) %>% group_by (gender) %>% summarise(n= n()) %>% ungroup() # 183 female, 17 male
gender n
Female 183
Male 17
ghasemi_data %>% dplyr::select (age, cog_ability) %>% skimr::skim() # mean and sd for age and cognitive ability
Data summary
Name Piped data
Number of rows 38400
Number of columns 2
_______________________
Column type frequency:
numeric 2
________________________
Group variables None

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
age 0 1 22.20 6.78 17 19 20 22 52 ▇▁▁▁▁
cog_ability 0 1 39.55 9.46 11 34 40 46 61 ▁▃▇▆▂

5 Data Analysis

5.1 t-test

Now, we use the treatment data to run three different independent t-tests. Suppose we did an experiment to compare the effectiveness of CBT vs. Psychodynamic therapies in decreasing anxiety, and depression and also in improving life satisfaction:

# t.test (indep)
t.test(anxiety~treatment, data= treatment_data)
## 
##  Welch Two Sample t-test
## 
## data:  anxiety by treatment
## t = -0.85021, df = 124.18, p-value = 0.3968
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -12.11096   4.83264
## sample estimates:
##           mean in group CBT mean in group Psychodynamic 
##                    60.54545                    64.18462
t.test(depression~treatment, data= treatment_data)
## 
##  Welch Two Sample t-test
## 
## data:  depression by treatment
## t = -2.8725, df = 123.97, p-value = 0.004792
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -18.21965  -3.35424
## sample estimates:
##           mean in group CBT mean in group Psychodynamic 
##                    47.15152                    57.93846
t.test(life_satisfaction~treatment, data= treatment_data)
## 
##  Welch Two Sample t-test
## 
## data:  life_satisfaction by treatment
## t = -5.2688, df = 127.11, p-value = 0.0000005699
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -27.61850 -12.53721
## sample estimates:
##           mean in group CBT mean in group Psychodynamic 
##                    31.06061                    51.13846

In another experiment, suppose we have created a method to boost memory. Then, we recruit some participants, do a memory pre-test, implement the method, and do a memory post-test, Now, we want to see whether our method have improved participants’ memory:

# t.test (paired)
t.test(memory_score~time, data= memory_data, paired= T)
## 
##  Paired t-test
## 
## data:  memory_score by time
## t = 5.4761, df = 130, p-value = 0.0000002163
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##   7.333171 15.628661
## sample estimates:
## mean of the differences 
##                11.48092

Now that we learned about t-test, let’s perform this test on our dataset. Is there a difference between groups at the first stage? Ideally, we want participants’ depresion score at the first stage to be similar for both groups because we have not started our treatment yet. Previous graphs showed us that depression scores of the CBT and Psychodynamic groups at this stage are pretty close. Let’s test that using an independent t-test (because we have 2 independent groups):

# Is there a difference between groups at the first stage?
data_exp1 %>% 
  group_by(group) %>% 
  filter(stage=='stage1') %>% 
  ungroup () %>%
  t.test(depression_score~group, data = ., paired=FALSE)
## 
##  Welch Two Sample t-test
## 
## data:  depression_score by group
## t = 0.10768, df = 118.92, p-value = 0.9144
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -9.205588 10.264329
## sample estimates:
##           mean in group CBT mean in group Psychodynamic 
##                    53.59091                    53.06154

Now, we wonder if psychotherapy treatments were effective at all, regardless of the treatment method. So, we would like to test if depresion score at the forth stage are lower than scores at the stage 2? Since a pair of score at stage 2 and stage 4 is coming from a same person, we use paired t-test.

# Is there a difference between ratings of stage2 and stage4?
data_exp1 %>% 
  filter(stage=='stage2' | stage=='stage4') %>% 
  ungroup () %>%
  t.test(depression_score~stage, data = ., paired=TRUE)
## 
##  Paired t-test
## 
## data:  depression_score by stage
## t = 5.5931, df = 130, p-value = 0.0000001261
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##   7.70108 16.13098
## sample estimates:
## mean of the differences 
##                11.91603
  • Exercise: John et al. (2019) investigated the consequences of backing down (changing one’s mind in lights of evidence)and how other people view someone who change their mind. In their second experiments, they presented participants either with a person who changes their mind or a person who refuses to back down. Then, they asked participants to rate how intelligent and confident the person is (See the original study here). They reported that:

“Relative to the entrepreneur who did not back down, participants judged the entrepreneur who backed down as more intelligent (M_backed_down=5.13 out of 7, SD=1.09; M_did_not_back_down=3.97, SD=1.54; t(271.12)=−7.59, p < .001) but less confident (M_backed_down=4.50 out of 7, SD=1.36; M_did_not_back_down=5.65, SD=1.10; t(291.01)=8.08, p < .001).”.

Open the john_backdown_exp2.csv file and try to reproduce their results. Run two separate independent t-test, one with intelligent as the dependent variable and one with confident as the dependent variable. For both t-test, use back_down as the between-subject independent variable.

john_data <- read_csv(here("cleaned_data","john_backdown_exp2.csv"))


t.test(intelligent~back_down, data = john_data, paired=FALSE)
## 
##  Welch Two Sample t-test
## 
## data:  intelligent by back_down
## t = 7.5853, df = 271.12, p-value = 0.0000000000005319
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  0.8577107 1.4590076
## sample estimates:
##       mean in group backed_down mean in group did_not_back_down 
##                        5.129412                        3.971053
t.test(confident~back_down, data = john_data, paired=FALSE)
## 
##  Welch Two Sample t-test
## 
## data:  confident by back_down
## t = -8.0763, df = 291.01, p-value = 0.00000000000001787
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -1.4257768 -0.8670294
## sample estimates:
##       mean in group backed_down mean in group did_not_back_down 
##                        4.503268                        5.649671

5.2 Analysis of Variance (ANOVA)

Now, let’s analysis our main experiment data: Do participants in the CBT group show better outcome compared to participants in the Psychodynamic group? Suppose we believe that participants should show lower depression after 5 or 10 sessions of both psychotherapy treatments and this decrease should be more pronounced for CBT than psychodynamic psychotherapy. If this is the case. we expect an interaction in the traditional Analysis of Variance (AONVA) test.

aov_m1 <- aov_car (depression_score ~ group*stage +
                     Error(subject/stage), data = data_exp1)  
Effect df MSE F ges p.value
group 1, 129 737.60 27.08 *** .066 <.001
stage 2.97, 382.72 492.81 53.15 *** .215 <.001
group:stage 2.97, 382.72 492.81 8.91 *** .044 <.001

As you can see, we found a significant main effect of stage and a significant group by stage interaction. We can use the emmeans package to do post-hoc tests.

# main effect of stage
emmeans(aov_m1, 'stage')
##  stage  emmean   SE  df lower.CL upper.CL
##  stage1   53.3 1.83 579     49.7     56.9
##  stage2   33.3 1.83 579     29.7     36.9
##  stage3   26.3 1.83 579     22.7     29.9
##  stage4   21.4 1.83 579     17.8     25.0
##  stage5   31.4 1.83 579     27.8     35.0
## 
## Results are averaged over the levels of: group 
## Warning: EMMs are biased unless design is perfectly balanced 
## Confidence level used: 0.95
pairs(emmeans(aov_m1, 'stage'), adjust= 'holm')
##  contrast        estimate   SE  df t.ratio p.value
##  stage1 - stage2    20.03 2.36 516  8.480  <.0001 
##  stage1 - stage3    26.94 2.36 516 11.404  <.0001 
##  stage1 - stage4    31.91 2.36 516 13.506  <.0001 
##  stage1 - stage5    21.84 2.36 516  9.245  <.0001 
##  stage2 - stage3     6.91 2.36 516  2.924  0.0144 
##  stage2 - stage4    11.87 2.36 516  5.027  <.0001 
##  stage2 - stage5     1.81 2.36 516  0.766  0.4442 
##  stage3 - stage4     4.97 2.36 516  2.102  0.0941 
##  stage3 - stage5    -5.10 2.36 516 -2.158  0.0941 
##  stage4 - stage5   -10.07 2.36 516 -4.261  0.0001 
## 
## Results are averaged over the levels of: group 
## P value adjustment: holm method for 10 tests
# group by stage interaction
emmeans(aov_m1, "group", by= "stage")
## stage = stage1:
##  group         emmean   SE  df lower.CL upper.CL
##  CBT             53.5 2.59 577    48.47     58.6
##  Psychodynamic   53.0 2.60 580    47.92     58.1
## 
## stage = stage2:
##  group         emmean   SE  df lower.CL upper.CL
##  CBT             30.7 2.59 577    25.58     35.7
##  Psychodynamic   35.9 2.60 580    30.75     41.0
## 
## stage = stage3:
##  group         emmean   SE  df lower.CL upper.CL
##  CBT             21.7 2.59 577    16.62     26.8
##  Psychodynamic   31.0 2.60 580    25.89     36.1
## 
## stage = stage4:
##  group         emmean   SE  df lower.CL upper.CL
##  CBT             13.4 2.59 577     8.29     18.4
##  Psychodynamic   29.4 2.60 580    24.29     34.5
## 
## stage = stage5:
##  group         emmean   SE  df lower.CL upper.CL
##  CBT             18.8 2.59 577    13.74     23.9
##  Psychodynamic   44.1 2.60 580    38.96     49.2
## 
## Warning: EMMs are biased unless design is perfectly balanced 
## Confidence level used: 0.95
update(pairs(emmeans(aov_m1, "group", by= "stage")), by = NULL, adjust = "holm") 
##  contrast            stage  estimate   SE  df t.ratio p.value
##  CBT - Psychodynamic stage1    0.529 3.67 579  0.144  0.8852 
##  CBT - Psychodynamic stage2   -5.195 3.67 579 -1.417  0.3138 
##  CBT - Psychodynamic stage3   -9.288 3.67 579 -2.534  0.0346 
##  CBT - Psychodynamic stage4  -16.022 3.67 579 -4.371  0.0001 
##  CBT - Psychodynamic stage5  -25.244 3.67 579 -6.887  <.0001 
## 
## P value adjustment: holm method for 5 tests

You can use the afex_plot function from afex to create beautiful plots. Those plots interacts nicely with ggplot:

afex_plot(aov_m1, x = "stage", trace = "group", error='between',
          line_arg = list(size=1),
          point_arg = list(size=3.5),
          data_arg = list(size= 1, color= 'grey', width=.4),
          data_geom = geom_boxplot,
          mapping = c("linetype", "shape", "fill"),
          legend_title = "Group") +
  labs(y = "Depression Score", x = "") +
  theme_bw()+ # remove the grey background and grid
  theme(axis.text=element_text(size=13),
        axis.title = element_text(size = 13),
        legend.text=element_text(size=13),
        legend.title=element_text(size=13),
        legend.position='bottom',
        legend.key.size = unit(1, "cm"),
        legend.background = element_rect(colour = 'black', fill = 'white', linetype='solid'))+
  scale_color_simpsons() +
  scale_fill_simpsons()

If you are interested in this topic, check out this nice tutorial about using afex to run ANOVA, and also this interesting tutorial on the emmeans package.

  • Exercise: Rotello et al. (2018) investigated the association between the race (White vs. Black faces) and the gun-tool judgments. In their first experiments, they presented participants with 16 White male faces and 16 Black male faces, and following that 8 images of guns and 8 images of tools. They asked participants to judge if the object is a tool or a gun by pressing keyboard buttons. Then, they ran an ANOVA to see if participants’ gun responses are higher for any of the races. So, they included prime race (Black, White) and target identity (gun, tool) as independent variables and participants’ gun responses as dependent variable into their linear model (See the original study here). They found that:

“Participants made more gun responses to guns than to tools, F(1,45) = 53243, p < 0.0001, η2g = 0.998. However, the race of the prime face did not matter, F(1,45) = 0.287, p > 0.59, η2g = 0.001, nor was there an interaction of prime race with target object, F(1,45) = 0.022, p > 0.88, η2g = 0.000)”.

Open the rotello_shooter_exp1.csv file and try to reproduce their results. Run an ANOVA (type III) with resp as the dependent variable and target, prime, and their interaction as independent variables.

# load the general data file
rotello_data <- read_csv(here("cleaned_data","rotello_shooter_exp1.csv"))

# ANOVA
rotello_aov <- aov_car (resp ~ target*prime +
           Error(subject/target*prime), data = rotello_data)
Effect df MSE F ges p.value
target 1, 45 0.00 53242.99 *** .998 <.001
prime 1, 45 0.00 0.29 .001 .595
target:prime 1, 45 0.00 0.02 <.001 .883

5.3 Correlation

Here, we want to check the correlation between variables on the narcissism_data. First, we need to remove subject column because it is not numeric:

narcissism_data_cor <- narcissism_data %>%
  select(-subject)
#-- Base R:
cor(narcissism_data_cor, method = "pearson",  use = "complete.obs")

#-- Psych library:
psych::pairs.panels(narcissism_data_cor, method = "pearson", hist.col = "#00AFBB", density = T, ellipses = F, stars = T)

#-- Correlation library:
# install.packages("devtools")
# devtools::install_github("easystats/correlation")
#library("correlation")
correlation::correlation(narcissism_data_cor) %>% summary()

#-- apaTables library:
narcissism_data_cor %>% 
  apaTables::apa.cor.table(filename="./outputs/CorMatrix.doc", show.conf.interval=T)
psychopathy self_esteem narcissism mental_health
psychopathy 1.00 0.15 0.40 -0.44
self_esteem 0.15 1.00 0.11 -0.29
narcissism 0.40 0.11 1.00 -0.26
mental_health -0.44 -0.29 -0.26 1.00
Parameter mental_health narcissism self_esteem
psychopathy -0.44 0.40 0.15
self_esteem -0.29 0.11
narcissism -0.26
  • Exercise: Pennycook et al. (2020) investigated the relationship between actively open-minded thinking style about evidence (AOT-E) and different political, scientific, and religious beliefs (see the original paper here). In their first experiment, they calculated the correlation of AOTE and scientific beliefs items (global warming, evolution, etc.) and they found the following results:

Open the pennycook_aote_exp1.csv file and try to reproduce their results by creating the same correlation matrix.

pennycook_data <- read_csv(here("cleaned_data","pennycook_aote_exp1.csv")) 


#---------- Base R:
cor(pennycook_data, method = "pearson",  use = "complete.obs")

#---------- Psych library:
pennycook_data %>% 
  psych::pairs.panels(method = "pearson", hist.col = "#00AFBB", density = T, ellipses = F, stars = T)

#---------- Correlation library:
correlation::correlation(pennycook_data) %>% summary()

#---------- apaTables library:
pennycook_data %>% 
  apaTables::apa.cor.table(filename="./outputs/CorMatrix.doc", show.conf.interval=T)
Parameter trust_scien gm_health tech_problems modern_medicine old_earth vaccines stem_cell big_bang evolution global_warming
aote 0.35 0.36 0.44 0.33 0.40 0.47 0.45 0.51 0.51 0.37
global_warming 0.42 0.06 0.14 0.18 0.33 0.26 0.31 0.33 0.38
evolution 0.48 0.33 0.28 0.36 0.47 0.39 0.54 0.78
big_bang 0.49 0.37 0.28 0.36 0.45 0.37 0.54
stem_cell 0.47 0.34 0.36 0.47 0.40 0.40
vaccines 0.43 0.52 0.49 0.53 0.38
old_earth 0.29 0.24 0.21 0.33
modern_medicine 0.43 0.42 0.47
tech_problems 0.33 0.39
gm_health 0.31

5.4 Linear Regression

Here, we do single and multiple linear regreassion on the narcissism_data:

m1 <- lm(mental_health~narcissism, data= narcissism_data)
term estimate std.error statistic p.value
(Intercept) 4.86 0.56 8.75 0
narcissism -0.04 0.01 -3.04 0
m2 <- lm(mental_health~narcissism+psychopathy, data= narcissism_data)
term estimate std.error statistic p.value
(Intercept) 5.43 0.53 10.27 0.00
narcissism -0.02 0.01 -1.09 0.28
psychopathy -0.19 0.04 -4.71 0.00
  • Exercise: Trémolière and Djeriouat (2020) examined the role of cognitive reflection and belief in science in climate change skepticism. In their first study, they revealed that cognitive reflection and belief in science negetively predicted climate change skepticism even after controlling for demographic and cognitive ability variables (see the original paper here).

Open the tremoliere_data_exp1.csv file and try to reproduce their results by running a multiple linear regression. Enter age, gender, education, belief in science, literacy, numeracy (Numtotal), and cognitive reflection as predictors and enter climate change skepticism (climato) as the outcome variable.

Tremoliere_data <- read_csv(here("cleaned_data","tremoliere_data_exp1.csv"))

Tremoliere_reg=lm(Climato ~ Age+ Gender+ Education+ BeliefInSciencetotal+ Literacy+ Numtotal+ CognitiveReflection,
                    data=Tremoliere_data)
term estimate std.error statistic p.value
(Intercept) 57.57 5.19 11.09 0.00
Age 0.01 0.05 0.24 0.81
Gender -5.68 1.34 -4.23 0.00
Education 0.54 0.38 1.43 0.15
BeliefInSciencetotal -0.20 0.06 -3.62 0.00
Literacy -0.49 0.51 -0.96 0.34
Numtotal -1.52 0.83 -1.82 0.07
CognitiveReflection -18.58 4.26 -4.37 0.00
r.squared adj.r.squared sigma statistic p.value df logLik AIC BIC deviance df.residual nobs
0.19 0.17 12.65 11.91 0 7 -1467.77 2953.54 2988.81 58235.89 364 372

6 Rmarkdown

To be completed…

7 References

  • Ghasemi, O., Handley, S., & Howarth, S. (2020). The Bright Homunculus in our Head: Individual Differences in Intuitive Sensitivity to Logical Validity.

  • John, L. K., Jeong, M., Gino, F., & Huang, L. (2019). The self-presentational consequences of upholding one’s stance in spite of the evidence. Organizational Behavior and Human Decision Processes, 154, 1-14.

  • Pennycook, G., Cheyne, J. A., Koehler, D. J., & Fugelsang, J. A. (2020). On the belief that beliefs should change according to evidence: Implications for conspiratorial, moral, paranormal, political, religious, and science beliefs. Judgment and Decision Making, 15(4), 476.

  • Rotello, C. M., Kelly, L. J., Heit, E., Vazire, S., & Vul, E. (2018). The Shape of ROC Curves in Shooter Tasks: Implications for Best Practices in Analysis. Collabra: Psychology, 4(1).

  • Trémolière, B., & Djeriouat, H. (2020). Don’t you see that its cold! Exploring the roles of cognitive reflection, climate science literacy, illusion of knowledge, and political orientation in climate change skepticism.

  • Wickham, H. (2014). Tidy data. Journal of Statistical Software, 59(10), 1-23.

LS0tCnRpdGxlOiAiUiBmb3IgRGF0YSBBbmFseXNpcyIKYXV0aG9yOgogIC0gbmFtZTogIk9taWQgR2hhc2VtaSIKICAgIGFmZmlsaWF0aW9uOiBNYWNxdWFyaWUgVW5pdmVyc2l0eQogICAgZW1haWw6IG9taWRyZXphLmdoYXNlbWlAaGRyLm1xLmVkdS5hdQogIC0gbmFtZTogIk1haGRpIE1hemlkaSIKICAgIGFmZmlsaWF0aW9uOiBVbml2ZXJzaXR5IG9mIFdlc3Rlcm4gQXVzdHJhbGlhCiAgICBlbWFpbDogbWFoZGkubWF6aWRpc2hhcmFmYWJhZGlAcmVzZWFyY2gudXdhLmVkdS5hdQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIGtlZXBfbWQ6IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0aGVtZTogY2VydWxlYW4KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgICNjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBkZl9wcmludDogImthYmxlIgotLS0KClRoaXMgZG9jdW1lbnQgaXMgdGhlIHN1bW1hcnkgb2YgdGhlICoqUiBmb3IgRGF0YSBBbmFseXNpcyoqIHdvcmtzaG9wLiAKCkFsbCBjb3JyZXNwb25kZW5jZSByZWxhdGVkIHRvIHRoaXMgZG9jdW1lbnQgc2hvdWxkIGJlIGFkZHJlc3NlZCB0bzogCgo8Y2VudGVyPgpPbWlkIEdoYXNlbWkgKE1hY3F1YXJpZSBVbml2ZXJzaXR5LCBTeWRuZXksIE5TVywgMjEwOSwgQVVTVFJBTElBKSAKCkVtYWlsOiBvbWlkcmV6YS5naGFzZW1pQGhkci5tcS5lZHUuYXUgCjwvY2VudGVyPgoKCgo8c3R5bGU+Cgpib2R5eyAvKiBOb3JtYWwgICovCiAgICAgIHRleHQtYWxpZ246IGp1c3RpZnk7CiAgICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwp9CmNvZGUucnsgLyogQ29kZSBibG9jayAqLwogICAgZm9udC1zaXplOiAxNHB4Owp9CnByZSB7IC8qIENvZGUgYmxvY2sgLSBkZXRlcm1pbmVzIGNvZGUgc3BhY2luZyBiZXR3ZWVuIGxpbmVzICovCiAgICBmb250LXNpemU6IDEycHg7Cn0KCjwvc3R5bGU+CgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy5hbGlnbj0iY2VudGVyIikKYGBgCgoKCmBgYHtyIGxpYnJhcmllcywgbWVzc2FnZT1GQUxTRSwgZWNobz1GfQojIGxvYWQgbGlicmFyaWVzCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGhlcmUpCmxpYnJhcnkoamFuaXRvcikKbGlicmFyeShicm9vbSkKbGlicmFyeShhZmV4KQpsaWJyYXJ5KGVtbWVhbnMpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShnZ3NjaSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoc2tpbXIpCiMgaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiZWFzeXN0YXRzL2NvcnJlbGF0aW9uIikKbGlicmFyeSgiY29ycmVsYXRpb24iKQpvcHRpb25zKHNjaXBlbj05OTkpICMgdHVybiBvZmYgc2NpZW50aWZpYyBub3RhdGlvbnMKb3B0aW9ucyhjb250cmFzdHMgPSBjKCdjb250ci5zdW0nLCdjb250ci5wb2x5JykpICMgc2V0IHRoZSBjb250cmFzdCBzdW0gZ2xvYmFsbHkgCm9wdGlvbnMoa25pdHIua2FibGUuTkEgPSAnJykKYGBgCgoKIyBJbnRyb2R1Y3Rpb24gdG8gUgoKIyMgQmFzaWNzIGFuZCBWYXJpYWJsZXMKCmBgYHtyIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNzAwcHgiLCBvdXQuaGVpZ2h0PSI3MDBweCIsIGZpZy5jYXA9ICJBcnR3b3JrIGJ5IEFsbGlzb24gSG9yc3Q6IGh0dHBzOi8vZ2l0aHViLmNvbS9hbGxpc29uaG9yc3Qvc3RhdHMtaWxsdXN0cmF0aW9ucyJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmUoJ2lucHV0cycsJ3JfZmlyc3RfdGhlbi5wbmcnKSkKYGBgCgoKUiBjYW4gYmUgdXNlZCBhcyBhIGNhbGN1bGF0b3IuIEZvciBtYXRoZW1hdGljYWwgcHVycG9zZXMsIGJlIGNhcmVmdWwgb2YgdGhlIG9yZGVyIGluIHdoaWNoIFIgZXhlY3V0ZXMgdGhlIGNvbW1hbmRzLgoKYGBge3J9CjEwICsgMTAKCjQgXiAyCgooMjUwIC8gNTAwKSAqIDEwMApgYGAKClIgaXMgYSBiaXQgZmxleGlibGUgd2l0aCBzcGFjaW5nIChidXQgbm8gc3BhY2luZyBpbiB0aGUgbmFtZSBvZiB2YXJpYWJsZXMgYW5kIHdvcmRzKQoKYGBge3J9CjEwKzEwCgoxMCAgICAgICAgICAgICAgICAgKyAgICAgICAgICAgMTAKYGBgCgpSIGNhbiBzb21ldGltZXMgdGVsbCB0aGF0IHlvdSdyZSBub3QgZmluaXNoZWQgeWV0CgpgYGB7ciBldmFsPUZ9CjEwICsKYGBgCgpIb3cgdG8gY3JlYXRlIGEgKnZhcmlhYmxlKj8gVmFyaWFibGUgYXNzaWdubWVudCB1c2luZyBgPC1gIGFuZCBgPWAuIE5vdGUgdGhhdCBSIGlzIGNhc2Ugc2Vuc2l0aXZlIGZvciBldmVyeXRoaW5nCgpgYGB7cn0KcGF5IDwtIDI1MAoKbW9udGggPSAxMgoKcGF5ICogbW9udGgKCnNhbGFyeSA8LSBwYXkgKiBtb250aApgYGAKCgpGZXcgcG9pbnRzIGluIG5hbWluZyB2YXJpYWJsZXMgYW5kIHZlY3RvcnM6IHVzZSBzaG9ydCwgaW5mb3JtYXRpdmUgd29yZHMsIGtlZXAgc2FtZSBtZXRob2QgKGUuZy4sIHlvdSBjYW4gdXNlIGNhcGl0YWwgbGV0dGVycyBidXQgaXQgaXMgbm90IHJlY29tbWVuZGVkLCB1c2Ugb25seSBfIG9yIC4gKS4KCiMjIEZ1bmN0aW9uIApGdW5jdGlvbiBpcyBhIHNldCBvZiBzdGF0ZW1lbnRzIGNvbWJpbmVkIHRvZ2V0aGVyIHRvIHBlcmZvcm0gYSBzcGVjaWZpYyB0YXNrLiBXaGVuIHdlIHVzZSBhIGJsb2NrIG9mIGNvZGUgcmVwZWF0ZWRseSwgd2UgY2FuIGNvbnZlcnQgaXQgdG8gYSBmdW5jdGlvbi4gVG8gd3JpdGUgYSBmdW5jdGlvbiwgZmlyc3QsIHlvdSBuZWVkIHRvICpkZWZpbmUqIGl0OgoKYGBge3J9Cm15X211bHRpcGxpZXIgPC0gZnVuY3Rpb24oYSxiKXsKICByZXN1bHQgPSBhICogYgogIHJldHVybiAocmVzdWx0KQp9CmBgYAoKVGhpcyBjb2RlIGRvIG5vdGhpbmcuIFRvIGdldCBhIHJlc3VsdCwgeW91IG5lZWQgdG8gKmNhbGwqIGl0OgoKYGBge3J9Cm15X211bHRpcGxpZXIgKGE9MiwgYj00KQojIG9yOiBteV9tdWx0aXBsaWVyICgyLCA0KQpgYGAKCldlIGNhbiBzZXQgYSBkZWZhdWx0IHZhbHVlIGZvciBvdXIgYXJndW1lbnRzOgoKYGBge3J9Cm15X211bHRpcGxpZXIyIDwtIGZ1bmN0aW9uKGEsYj00KXsKICByZXN1bHQgPSBhICogYgogIHJldHVybiAocmVzdWx0KQp9CgpteV9tdWx0aXBsaWVyMiAoYT0yKQojIG9yOiBteV9tdWx0aXBsaWVyICgyKQojIG9yOiBteV9tdWx0aXBsaWVyICgyLCA2KQpgYGAKCkZvcnR1bmF0ZWx5LCB5b3UgZG8gbm90IG5lZWQgdG8gd3JpdGUgZXZlcnl0aGluZyBmcm9tIHNjcmF0Y2guIFIgaGFzIGxvdHMgb2YgYnVpbHQtaW4gZnVuY3Rpb25zIHRoYXQgeW91IGNhbiB1c2U6CmBgYHtyfQpyb3VuZCg1NC42Nzg3KQpyb3VuZCg1NC41Nzg3LCBkaWdpdHMgPSAyKQpgYGAKClVzZSBgP2AgYmVmb3JlIHRoZSBmdW5jdGlvbiBuYW1lIHRvIGdldCBzb21lIGhlbHAuIEZvciBleGFtcGxlLCBgP3JvdW5kYC4gWW91IHdpbGwgc2VlIG1hbnkgZnVuY3Rpb25zIGluIHRoZSByZXN0IG9mIHRoZSB3b3Jrc2hvcC4KCiMjIERhdGEgVHlwZXMKCmZ1bmN0aW9uIGBjbGFzcygpYCBpcyB1c2VkIHRvIHNob3cgd2hhdCBpcyB0aGUgdHlwZSBvZiBhIHZhcmlhYmxlLgoKCjEuICpMb2dpY2FsKjogYFRSVUVgLCBgRkFMU0VgIGNhbiBiZSBhYmJyZXZpYXRlZCBhcyBgVGAsIGBGYC4gIFRoZXkgaGFzIHRvIGJlIGNhcGl0YWwsICd0cnVlJyBpcyBub3QgYSBsb2dpY2FsIGRhdGE6CmBgYHtyfQpjbGFzcyhUUlVFKQpjbGFzcyhGKQpgYGAKCjIuICpOdW1lcmljKjogYWxsIG51bWJlcnMgZS5nLiA1LCAgMTAuNSwgIDExLDM3OyAgYSBzcGVjaWFsIHR5cGUgb2YgbnVtZXJpYyBpcyAiaW50ZWdlciIgd2hpY2ggaXMgbnVtYmVycyB3aXRob3V0IGRlY2ltYWwuIEludGVnZXJzIGFyZSBhbHdheXMgbnVtZXJpYywgYnV0IG51bWVyaWMgaXMgbm90IGFsd2F5cyBpbnRlZ2VyOgpgYGB7cn0KY2xhc3MoMikKY2xhc3MoMTMuNDYpCmBgYAoKMy4gKkNoYXJhY3Rlcio6IHRleHQgZm9yIGV4YW1wbGUsICJJIGxvdmUgUiIgb3IgIjQiIG9yICI0LjUiOgpgYGB7cn0KY2xhc3MoImhhIGhhIGhhIGhhIikKY2xhc3MoIjU2LjYiKQpjbGFzcygiVFJVRSIpCmBgYAoKQ2FuIHdlIGNoYW5nZSB0aGUgdHlwZSBvZiBkYXRhIGluIGEgdmFyaWFibGU/IFllcywgeW91IG5lZWQgdG8gdXNlIHRoZSBmdW5jdGlvbiBgYXMuLS0tKClgCgpgYGB7cn0KYXMubnVtZXJpYyhUUlVFKQphcy5jaGFyYWN0ZXIoNCkKYXMubnVtZXJpYygiNC41IikKYXMubnVtZXJpYygiSGVsbG8iKQpgYGAKCgojIyBEYXRhIFN0cnVjdHVyZXMKCgojIyMgVmVjdG9yIAoKV2hlbiB0aGVyZSBhcmUgbW9yZSB0aGFuIG9uZSBudW1iZXIgb3IgbGV0dGVyIHN0b3JlZC4gVXNlIHRoZSBjb21iaW5lIGZ1bmN0aW9uIGMoKSBmb3IgdGhhdC4KCmBgYHtyfQpzYWxlIDwtIGMoMSwgMiwgMyw0LCA1LCA2LCA3LCA4LCA5LCAxMCkgIyBhbHNvIHNhbGUgPC0gYygxOjEwKQoKc2FsZSA8LSBjKDE6MTApCgpzYWxlICogc2FsZQpgYGAKCipTdWJzZXR0aW5nIGEgdmVjdG9yKjoKCmBgYHtyfQpkYXlzIDwtIGMoIlNhdHVyZGF5IiwgIlN1bmRheSIsICJNb25kYXkiLCAiVHVlc2RheSIsICJXZWRuZXNkYXkiLCAiVGh1cnNkYXkiLCAiRnJpZGF5IikKCmRheXNbMl0KZGF5c1stMl0KCmRheXNbYygyLCAzLCA0KV0KYGBgCgoKKiAqRXhlcmNpc2UqOiBDcmVhdGUgYSB2ZWN0b3IgbmFtZWQgYG15X3ZlY3RvcmAgd2l0aCBudW1iZXJzIGZyb20gMCB0byAxMDAwIGluIGl0IGFuZCBjYWxjdWxhdGUgbWVhbiwgbWVkaWFuLCBzZCwgbWluLCBtYXgsIGFuZCBzdW0gb2YgdGhhdCB2ZWN0b3I6CgpgYGB7cn0KbXlfdmVjdG9yIDwtICgwOjEwMDApCgptZWFuKG15X3ZlY3RvcikKbWVkaWFuKG15X3ZlY3RvcikKbWluKG15X3ZlY3RvcikKcmFuZ2UobXlfdmVjdG9yKQpjbGFzcyhteV92ZWN0b3IpCnN1bShteV92ZWN0b3IpCnNkKG15X3ZlY3RvcikKYGBgCgojIyMgTGlzdAoKTGlzdCBhbGxvd3MgeW91IHRvIGdhdGhlciBhIHZhcmlldHkgb2Ygb2JqZWN0cyB1bmRlciBvbmUgbmFtZSAodGhhdCBpcywgdGhlIG5hbWUgb2YgdGhlIGxpc3QpIGluIGFuIG9yZGVyZWQgd2F5LiBUaGVzZSBvYmplY3RzIGNhbiBiZSBtYXRyaWNlcywgdmVjdG9ycywgZGF0YSBmcmFtZXMsIGV2ZW4gb3RoZXIgbGlzdC4KCmBgYHtyfQpteV9saXN0ID0gbGlzdChzYWxlLCAxLCAzLCA0OjcsICJIRUxMTyIsICJoZWxsbyIsIEZBTFNFKQpteV9saXN0CmBgYAoKIyMjIEZhY3RvcgpGYWN0b3JzIHN0b3JlIHRoZSB2ZWN0b3IgYWxvbmcgd2l0aCB0aGUgZGlzdGluY3QgdmFsdWVzIG9mIHRoZSBlbGVtZW50cyBpbiB0aGUgdmVjdG9yIGFzIGxhYmVscy4gVGhlIGxhYmVscyBhcmUgYWx3YXlzIGNoYXJhY3RlciBpcnJlc3BlY3RpdmUgb2Ygd2hldGhlciBpdCBpcyBudW1lcmljIG9yIGNoYXJhY3Rlci4gRm9yIGV4YW1wbGUsIHZhcmlhYmxlIGdlbmRlciB3aXRoICJtYWxlIiBhbmQgImZlbWFsZSIgZW50cmllczoKCmBgYHtyfQpnZW5kZXIgPC0gYygibWFsZSIsICJtYWxlIiwgIm1hbGUiLCAiIGZlbWFsZSIsICJmZW1hbGUiLCAiZmVtYWxlIikKZ2VuZGVyIDwtIGZhY3RvcihnZW5kZXIpCmBgYAoKUiBub3cgdHJlYXRzIGdlbmRlciBhcyBhIG5vbWluYWwgKGNhdGVnb3JpY2FsKSB2YXJpYWJsZTogMT1mZW1hbGUsIDI9bWFsZSBpbnRlcm5hbGx5IChhbHBoYWJldGljYWxseSkuCmBgYHtyfQpzdW1tYXJ5KGdlbmRlcikKYGBgCgoqICpRdWVzdGlvbio6IHdoeSB3aGVuIHdlIHJhbiB0aGUgYWJvdmUgZnVuY3Rpb24gaS5lLiBzdW1tYXJ5KCksIGl0IHNob3dlZCB0aHJlZSBhbmQgbm90IHR3byBsZXZlbHMgb2YgdGhlIGRhdGE/ICpIaW50KjogcnVuICdnZW5kZXInLgoKYGBge3J9CmdlbmRlcgpgYGAKClNvLCBiZSBjYXJlZnVsIG9mIHNwYWNlcyEKCiogKkV4ZXJjaXNlKjogQ3JlYXRlIGEgZ2VuZGVyIGZhY3RvciB3aXRoIDMwIG1hbGUgYW5kIDQwIGZlbWFsZXMgKCpIaW50KjogdXNlIHRoZSBgcmVwKClgIGZ1bmN0aW9uKToKYGBge3J9CmdlbmRlciA8LSBjKHJlcCgibWFsZSIsMzApLCByZXAoImZlbWFsZSIsIDQwKSkKZ2VuZGVyIDwtIGZhY3RvcihnZW5kZXIpCmdlbmRlcgpgYGAKClRoZXJlIGFyZSB0d28gdHlwZXMgb2YgY2F0ZWdvcmljYWwgdmFyaWFibGVzOiBub21pbmFsIGFuZCBvcmRpbmFsLiBIb3cgdG8gY3JlYXRlIG9yZGVyZWQgZmFjdG9ycyAod2hlbiB0aGUgdmFyaWFibGUgaXMgbm9taW5hbCBhbmQgdmFsdWVzIGNhbiBiZSBvcmRlcmVkKT8gV2Ugc2hvdWxkIGFkZCB0d28gYWRkaXRpb25hbCBhcmd1bWVudHMgdG8gdGhlIGBmYWN0b3IoKWAgZnVuY3Rpb246IGBvcmRlcmVkID0gVFJVRWAsIGFuZCBgbGV2ZWxzID0gYygibGV2ZWwxIiwgImxldmVsMiIpYC4gRm9yIGV4YW1wbGUsIHdlIGhhdmUgYSB2ZWN0b3IgdGhhdCBzaG93cyBwYXJ0aWNpcGFudHMnIGVkdWNhdGlvbiBsZXZlbC4KCmBgYHtyfQplZHU8LWMoMywyLDMsNCwxLDIsMiwzLDQpCgplZHVjYXRpb248LWZhY3RvcihlZHUsIG9yZGVyZWQgPSBUUlVFKQpsZXZlbHMoZWR1Y2F0aW9uKSA8LSBjKCJQcmltYXJ5IHNjaG9vbCIsImhpZ2ggc2Nob29sIiwiQ29sbGVnZSIsIlVuaSBncmFkdWF0ZWQiKQplZHVjYXRpb24KYGBgCgoqICpFeGVyY2lzZSo6IFdlIGhhdmUgYSBmYWN0b3Igd2l0aCBgcGF0aWVudGAgYW5kIGBjb250cm9sYCB2YWx1ZXMuIEhlcmUsIHRoZSBmaXJzdCBsZXZlbCBpcyBjb250cm9sIGFuZCB0aGUgc2Vjb25kIGxldmVsIGlzIHBhdGllbnQuIENoYW5nZSB0aGUgb3JkZXIgb2YgbGV2ZWxzLCBzbyBwYXRpZW50IHdvdWxkIGJlIHRoZSBmaXJzdCBsZXZlbDoKCmBgYHtyfQpoZWFsdGhfc3RhdHVzIDwtIGZhY3RvcihjKHJlcCgncGF0aWVudCcsNSkscmVwKCdjb250cm9sJyw1KSkpCmhlYWx0aF9zdGF0dXMKCmhlYWx0aF9zdGF0dXNfcmVvcmRlcmVkIDwtIGZhY3RvcihoZWFsdGhfc3RhdHVzLCBsZXZlbHMgPSBjKCdwYXRpZW50JywnY29udHJvbCcpKQpoZWFsdGhfc3RhdHVzX3Jlb3JkZXJlZApgYGAKCkZpbmFsbHksIGNhbiB5b3UgcmVsYWJlbCBib3RoIGxldmVscyB0byB1cHBlcmNhc2UgY2hhcmFjdGVycz8gKCpIaW50KjogY2hlY2sgYD9mYWN0b3JgKQoKYGBge3J9CmhlYWx0aF9zdGF0dXNfcmVsYWJlbGVkIDwtIGZhY3RvcihoZWFsdGhfc3RhdHVzLCBsZXZlbHMgPSBjKCdwYXRpZW50JywnY29udHJvbCcpLCBsYWJlbHMgPSBjKCdQYXRpZW50JywnQ29udHJvbCcpKQpoZWFsdGhfc3RhdHVzX3JlbGFiZWxlZApgYGAKCgojIyMgTWF0cmljZXMKQWxsIGNvbHVtbnMgaW4gYSBtYXRyaXggbXVzdCBoYXZlIHRoZSBzYW1lIG1vZGUobnVtZXJpYywgY2hhcmFjdGVyLCBldGMuKSBhbmQgdGhlIHNhbWUgbGVuZ3RoLiBJdCBjYW4gYmUgY3JlYXRlZCB1c2luZyBhIHZlY3RvciBpbnB1dCB0byB0aGUgbWF0cml4IGZ1bmN0aW9uLgoKYGBge3J9Cm15X21hdHJpeCA9IG1hdHJpeChjKDEsMiwzLDQsNSw2LDcsOCw5KSwgbnJvdyA9IDMsIG5jb2wgPSAzKQoKbXlfbWF0cml4CmBgYAoKIyMjIERhdGEgZnJhbWVzIAoKRGF0YSBmcmFtZXMgY2FuIGhvbGQgbnVtZXJpYywgY2hhcmFjdGVyIG9yIGxvZ2ljYWwgdmFsdWVzLiBXaXRoaW4gYSBjb2x1bW4gYWxsIGVsZW1lbnRzIGhhdmUgdGhlIHNhbWUgZGF0YSB0eXBlLCBidXQgZGlmZmVyZW50IGNvbHVtbnMgY2FuIGJlIG9mIGRpZmZlcmVudCBkYXRhIHR5cGUuIExldCdzIGNyZWF0ZSBhIGRhdGFmcmFtZToKCmBgYHtyfQppZCA8LSAxOjIwMApncm91cCA8LSBjKHJlcCgiUHN5Y2hvdGhlcmFweSIsIDEwMCksIHJlcCgiTWVkaWNhdGlvbiIsIDEwMCkpCnJlc3BvbnNlIDwtIGMocm5vcm0oMTAwLCBtZWFuID0gMzAsIHNkID0gNSksCiAgICAgICAgICAgICBybm9ybSgxMDAsIG1lYW4gPSAyNSwgc2QgPSA1KSkKCm15X2RhdGFmcmFtZSA8LWRhdGEuZnJhbWUoUGF0aWVudCA9IGlkLAogICAgICAgICAgICAgICAgICAgICAgICAgIFRyZWF0bWVudCA9IGdyb3VwLAogICAgICAgICAgICAgICAgICAgICAgICAgIFJlc3BvbnNlID0gcmVzcG9uc2UpCmBgYAoKV2UgYWxzbyBjb3VsZCBoYXZlIGRvbmUgdGhlIGJlbG93CgpgYGB7cn0KbXlfZGF0YWZyYW1lIDwtZGF0YS5mcmFtZShQYXRpZW50ID0gYygxOjIwMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgVHJlYXRtZW50ID0gYyhyZXAoIlBzeWNob3RoZXJhcHkiLCAxMDApLCByZXAoIk1lZGljYXRpb24iLCAxMDApKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBSZXNwb25zZSA9IGMocm5vcm0oMTAwLCBtZWFuID0gMzAsIHNkID0gNSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJub3JtKDEwMCwgbWVhbiA9IDI1LCBzZCA9IDUpKSkKYGBgCgpJbiBsYXJnZSBkYXRhIHNldHMsIHRoZSBmdW5jdGlvbiBoZWFkKCkgZW5hYmxlcyB5b3UgdG8gc2hvdyB0aGUgZmlyc3Qgb2JzZXJ2YXRpb25zIG9mIGEgZGF0YSBmcmFtZXMuIFNpbWlsYXJseSwgdGhlIGZ1bmN0aW9uIHRhaWwoKSBwcmludHMgb3V0IHRoZSBsYXN0IG9ic2VydmF0aW9ucyBpbiB5b3VyIGRhdGEgc2V0LgoKYGBge3IgZXZhbD1GfQpoZWFkKG15X2RhdGFmcmFtZSkgCnRhaWwobXlfZGF0YWZyYW1lKQpgYGAKCmBgYHtyIGVjaG89Rn0KaGVhZChteV9kYXRhZnJhbWUpICU+JQogIG11dGF0ZShgIGA9IGMoMTo2KSkgJT4lCiAgc2VsZWN0KGAgYCwgUGF0aWVudCwgVHJlYXRtZW50LAlSZXNwb25zZSkgJT4lCiAga25pdHI6OmthYmxlKCkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiYm9yZGVyZWQiLCAiY29uZGVuc2VkIiksIGZpeGVkX3RoZWFkID0gVCwgZnVsbF93aWR0aCA9IFQpCgp0YWlsKG15X2RhdGFmcmFtZSklPiUKICBrbml0cjo6a2FibGUoKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJib3JkZXJlZCIsICJjb25kZW5zZWQiKSwgZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gVCkKYGBgCgpTaW1pbGFyIHRvIHZlY3RvcnMgYW5kIG1hdHJpY2VzLCBicmFja2V0cyBbXSBhcmUgdXNlZCB0byBzZWxlY3RzIGRhdGEgZnJvbSByb3dzIGFuZCBjb2x1bW5zIGluIGRhdGEuZnJhbWVzOgoKYGBge3J9Cm15X2RhdGFmcmFtZVszNSwgM10KYGBgCgoqICpFeGVyY2lzZSo6IEhvdyBjYW4gd2UgZ2V0IGFsbCBjb2x1bW5zLCBidXQgb25seSBmb3IgdGhlIGZpcnN0IDEwIHBhcnRpY2lwYW50cz8KCmBgYHtyIGV2YWw9Rn0KbXlfZGF0YWZyYW1lWzE6MTAsIF0KYGBgCgpgYGB7ciBlY2hvPUZ9CmtuaXRyOjprYWJsZShteV9kYXRhZnJhbWVbMToxMCwgXSkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiYm9yZGVyZWQiLCAiY29uZGVuc2VkIiksIGZpeGVkX3RoZWFkID0gVCwgZnVsbF93aWR0aCA9IFQpCgpgYGAKSG93IHRvIGdldCBvbmx5IHRoZSBSZXNwb25zZSBjb2x1bW4gZm9yIGFsbCBwYXJ0aWNpcGFudHM/CgpgYGB7cn0KbXlfZGF0YWZyYW1lWyAsIDNdCmBgYAoKQW5vdGhlciBlYXNpZXIgd2F5IGZvciBzZWxlY3RpbmcgcGFydGljdWxhciBpdGVtcyBpcyB1c2luZyB0aGVpciBuYW1lcyB0aGF0IGlzIG1vcmUgaGVscGZ1bCB0aGFuIG51bWJlciBvZiB0aGUgcm93cyBpbiBsYXJnZSBkYXRhIHNldHM6CmBgYHtyIGV2YWw9Rn0KbXlfZGF0YWZyYW1lWyAsICJSZXNwb25zZSJdCiMgT1I6Cm15X2RhdGFmcmFtZSRSZXNwb25zZQoKYGBgCgpTbyBmYXIsIHdlIGNyZWF0ZWQgZGF0YWZyYW1lcyB1c2luZyBgZGF0YS5mcmFtZWAgZnVuY3Rpb24gZnJvbSB0aGUgYmFzZSBSLiBIb3dldmVyLCBhIGJldHRlciB3YXkgdG8gY3JlYXRlIGRhdGFmcmFtZXMgaXMgdG8gdXNlIHRoZSBgdGliYmxlYCBmdW5jdGlvbiBmcm9tIHRpZHl2ZXJzZSAoc2VlIFtoZXJlXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3RpYmJsZXMuaHRtbCkpLgoKIyBEYXRhIENsZWFuaW5nCgpgYGB7ciBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjcwMHB4Iiwgb3V0LmhlaWdodD0iMzUwcHgiLCBmaWcuY2FwPSAiQXJ0d29yayBieSBBbGxpc29uIEhvcnN0OiBodHRwczovL2dpdGh1Yi5jb20vYWxsaXNvbmhvcnN0L3N0YXRzLWlsbHVzdHJhdGlvbnMifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCdpbnB1dHMnLCdlbnZpcm9ubWVudGFsLWRhdGEtc2NpZW5jZS1yNGRzLWdlbmVyYWwucG5nJykpCmBgYAoKCmBgYHtyIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNzAwcHgiLCBvdXQuaGVpZ2h0PSIzNTBweCIsIGZpZy5jYXA9ICJBcnR3b3JrIGJ5IEFsbGlzb24gSG9yc3Q6IGh0dHBzOi8vZ2l0aHViLmNvbS9hbGxpc29uaG9yc3Qvc3RhdHMtaWxsdXN0cmF0aW9ucyJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmUoJ2lucHV0cycsJ2NyYWNrZWRfc2V0d2QucG5nJykpCmBgYAoKTm93LCBzdXBwb3NlIHdlIHJhbiBhbiBleHBlcmltZW50IHdpdGggMTQxIGRlcHJlc3NlZCBwYXRpZW50cy4gUGFydGljaXBhbnRzIHdlcmUgcmFuZG9tbHkgYXNzaWduZWQgaW50byB0d28gdHJlYXRtZW50IGdyb3VwczogQ0JUIG9yIFBzeWNob2R5bmFtaWMgcHN5Y2hvdGhlcmFweS4gV2UgbWVhc3VyZWQgc2VsZi1yZXBvcnQgZGVwcmVzc2lvbiBzY29yZXMgYXQgNSBkaWZmZXJlbnQgc3RhZ2VzIG9mIHRyZWF0bWVudDogCgotIFN0YWdlIDE6IEJlZm9yZSBzdGFydGluZyBhbnkgdHJlYXRtZW50LiBJdCBpcyBvdXIgYmFzZSBzdGFnZSAocHJlLXRlc3QpCi0gU3RhZ2UgMjogQWZ0ZXIgNSBzZXNzaW9ucyBvZiBwc3ljaG90aGVyYXB5IChwb3N0LXRlc3QxKQotIFN0YWdlIDM6IEFmdGVyIDEwIHNlc3Npb25zIG9mIHBzeWNob3RoZXJhcHkgKHBvc3QtdGVzdDIpCi0gU3RhZ2UgNDogQXQgdGhlIGVuZCBvZiB0aGUgdHJlYXRtZW50IChwb3N0LXRlc3QzKQotIFN0YWdlIDU6IFRocmVlIG1vbnRocyBhZnRlciB0aGUgdHJlYXRtZW50IChwb3N0LXRlc3Q0KQoKbGV0J3MgcmVhZCBhbmQgY2hlY2sgdGhlIHVuY2xlYW5lZCBkYXRhLiBCdXQsIGZpcnN0IHRoaW5nIGZpcnN0LiBsZXQncyBpbnN0YWxsIGFuZCB0aGVuIGxvYWQgdGhlIHRpZHl2ZXNlIHBhY2thZ2UuIFdlIGFsc28gbmVlZCBzb21lIG90aGVyIHBhY2thZ2VzOgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGV2YWw9Rn0KCiMgSW5zdGFsbCBpdAppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQoKIyBBbmQgdGhlbiBsb2FkIGl0CmxpYnJhcnkodGlkeXZlcnNlKQoKIyBMb2FkIG90aGVyIHBhY2thZ2VzIHRoYXQgeW91IGhhdmUgYWxyZWFkeSBpbnN0YWxsZWQKbGlicmFyeShoZXJlKQpsaWJyYXJ5KGphbml0b3IpCmxpYnJhcnkoYnJvb20pCmxpYnJhcnkoYWZleCkKbGlicmFyeShlbW1lYW5zKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkoZ2dzY2kpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KHNraW1yKQojIGluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImVhc3lzdGF0cy9jb3JyZWxhdGlvbiIpCmxpYnJhcnkoImNvcnJlbGF0aW9uIikKb3B0aW9ucyhzY2lwZW49OTk5KSAjIHR1cm4gb2ZmIHNjaWVudGlmaWMgbm90YXRpb25zCm9wdGlvbnMoY29udHJhc3RzID0gYygnY29udHIuc3VtJywnY29udHIucG9seScpKSAjIHNldCB0aGUgY29udHJhc3Qgc3VtIGdsb2JhbGx5IApvcHRpb25zKGtuaXRyLmthYmxlLk5BID0gJycpCmBgYAoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGV2YWw9Rn0KIyByZWFkIHRoZSByYXcgZGF0YQpyYXdfZGF0YSA8LSByZWFkX2NzdihoZXJlKCJyYXdfZGF0YSIsInJhd19kYXRhX2V4cDEuY3N2IikpCmhlYWQocmF3X2RhdGEpCmBgYAoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGVjaG89Rn0KIyByZWFkIHRoZSByYXcgZGF0YQpyYXdfZGF0YSA8LSByZWFkX2NzdihoZXJlKCJyYXdfZGF0YSIsInJhd19kYXRhX2V4cDEuY3N2IikpCgprbml0cjo6a2FibGUoaGVhZChyYXdfZGF0YSkpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImJvcmRlcmVkIiwgImNvbmRlbnNlZCIpLCBmaXhlZF90aGVhZCA9IFQsIGZ1bGxfd2lkdGggPSBGKSU+JQogIHNjcm9sbF9ib3god2lkdGggPSAiNzgwcHgiKQpgYGAKCiogKkV4ZXJjaXNlKjogVGhlcmUgaXMgYSBkYXRhc2V0IGluIHRoZSBgY2xlYW5lZF9kYXRhYCBmb2xkZXIgbmFtZWQgYHVuaWNlZl91NW1yLmNzdmAuIFJlYWQgdGhlIGRhdGFzZXQgdXNpbmcgYHJlYWRfY3N2YCBhbmQgYGhlcmVgLgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KdW5pY2VmX2RhdGEgPC0gcmVhZF9jc3YoaGVyZSgiY2xlYW5lZF9kYXRhIiwidW5pY2VmX3U1bXIuY3N2IikpCmBgYAoKYGBge3IgZWNobz1GQUxTRSwgb3V0LndpZHRoPSI3MDBweCIsIG91dC5oZWlnaHQ9IjM1MHB4IiwgZmlnLmNhcD0gIkFydHdvcmsgYnkgQWxsaXNvbiBIb3JzdDogaHR0cHM6Ly9naXRodWIuY29tL2FsbGlzb25ob3JzdC9zdGF0cy1pbGx1c3RyYXRpb25zIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZSgnaW5wdXRzJywndGlkeWRhdGFfMy5qcGcnKSkKYGBgCgpJbiBvcmRlciB0byBjbGVhbiB0aGUgZGF0YSwgd2UgdXNlICp0aWR5dmVyc2UqIHdoaWNoIGlzIGEgY29sbGVjdGlvbiBvZiBwYWNrYWdlcyB0byB3b3JrIHdpdGggZGF0YS4gT25lIG9mIHRoZSB0aWR5dmVyc2UgcGFja2FnZXMgdGhhdCB3ZSB1c2UgcmVndWxhcmx5IGlzIGBkcGx5cmAgd2hpY2ggaW5jbHVkZXMgc2V2ZXJhbCBmdW5jdGlvbnM6CgotIGBtdXRhdGUoKWAgYWRkcyBuZXcgdmFyaWFibGVzIG9yIGNoYW5nZSBleGlzdGluZyBvbmVzLgotIGBzZWxlY3QoKWAgcGljayB2YXJpYWJsZXMgKGNvbHVtbnMpIGJhc2VkIG9uIHRoZWlyIG5hbWVzLgotIGBmaWx0ZXIoKWAgcGlja3MgY2FzZXMgKHJvd3MpIGJhc2VkIG9uIHRoZWlyIHZhbHVlcy4KLSBgc3VtbWFyaXNlKClgIGdpdmVzIGEgc2luZ2xlIHNpbmdsZSBzdW1tYXJ5IG9mIHRoZSBkYXRhIChlLmcuLCBtZWFuLCBjb3VudHMsIGV0Yy4pCi0gYGFycmFuZ2UoKWAgY2hhbmdlcyB0aGUgb3JkZXJpbmcgb2YgdGhlIHJvd3MuCi0gYGdyb3VwX2J5KClgIGRpdmlkZXMgeW91ciBkYXRhZnJhbWUgaW50byBncm91cGVkIGRhdGFmcmFtZXMgYW5kIGFsbG93IHlvdSB0byBkbyBlYWNoIG9mIHRoZSBhYm92ZSBvcGVyYXRpb25zIChleGNlcHQgZm9yIGBhcnJhbmdlYCkgb24gZXZlcnkgb25lIG9mIHRoZW0gc2VwYXJhdGVseS4KCiMjIFNlbGVjdAoKUGljayBgc3ViamVjdGAsIGBhZ2VgLCBhbmQgYGdlbmRlcmAgY29sdW1uczoKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBldmFsPUZ9CnNlbGVjdGVkX2RhdGEgPC0gc2VsZWN0KHJhd19kYXRhLCBzdWJqZWN0LCBhZ2UsIGdlbmRlcikKYGBgCgojIyBGaWx0ZXIKTm93LCBkbyB0aGUgZm9sbG93aW5nIHRhc2tzOiBwaWNrIGFsbCB0aGUgbWFsZSBwYXJ0aWNpcGFudHMsIHBpY2sgYWxsIHRoZSBtYWxlIHBhcnRpY2lwYW50cyAqKm9yKiogdGhvc2UgZ3JlYXRlciB0aGFuIDI1IHllYXJzIG9sZCwgYW5kIGZpbmFsbHkgcGljayBhbGwgbWFsZSBwYXJ0aWNpcGFudHMgKiphbmQqKiB0aG9zZSBncmVhdGVyIHRoYW4gMjUgeWVhcnMgb2xkOgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGV2YWw9Rn0KIyBmaWx0ZXIgYWxsIG1hbGVzCmZpbF9tYWxlIDwtIGZpbHRlcihyYXdfZGF0YSwgZ2VuZGVyID09ICJNYWxlIikKIyBmaWx0ZXIgbWFsZXMgYW5kIG9sZGVyIHRoYW4gMjUKZmlsX21hbGVfYW5kX2cyNSA8LSBmaWx0ZXIocmF3X2RhdGEsIGdlbmRlciA9PSAiTWFsZSIgJiBhZ2UgPiAyNSApCiMgZmlsdGVyIG1hbGVzIG9yIG9sZGVyIHRoYW4gMjUKZmlsX21hbGVfb3JfZzI1IDwtIGZpbHRlcihyYXdfZGF0YSwgZ2VuZGVyID09ICJNYWxlIiB8IGFnZSA+IDI1ICkKYGBgCgojIyBBcnJhbmdlIApBcnJhbmdlIChvcmRlcikgeW91ciBkYXRhZnJhbWUgYmFzZWQgb24gdGhlIGFnZSwgb25jZSBpbiBhbiBhc2NlbmRpbmcgb3JkZXIgKHlvdW5nZXJzIGZpcnN0KSBhbmQgb25jZSBiYXNlZCBvbiBkZXNjZW5kaW5nIG9yZGVyIChvbGRlcnMgZmlyc3QpOgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CiMgb3JkZXIgcGFydGljaXBhbnRzIGJhc2VkIG9uIHRoZWlyIGFnZQphcnJhbmdlZF9kYXRhIDwtIGFycmFuZ2UocmF3X2RhdGEsIGFnZSkKIyBvcmRlciBwYXJ0aWNpcGFudHMgYmFzZWQgb24gdGhlaXIgYWdlIChkZXNjZW5kZWluZykKYXJyYW5nZWRfZGVzY2VuZGluZyA8LSBhcnJhbmdlKHJhd19kYXRhLCBkZXNjKGFnZSkpCmBgYAoKIyMgTXV0YXRlCkNyZWF0ZSBhIGNvbHVtbiB0byBzaG93IGlmIHRoZSBwYXJ0aWNpcGFudCBoYXMgZmluaXNoZWQgdGhlIHRhc2sgb3Igbm90OgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KbXV0YXRlZF9kYXRhIDwtIG11dGF0ZSAocmF3X2RhdGEsIGZpbmlzaGVkPSBjYXNlX3doZW4ocHJvZ3Jlc3M9PTEwMH4gIlllcyIsVH4gIk5vIikpCmBgYAoKIyMgU3VtbWFyaXNlClN1bW1hcml6ZSBwYXJ0aWNpcGFudHMgYWdlIGFuZCBzZDoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CnN1bW1hcmlzZShyYXdfZGF0YSwgbWVhbj0gbWVhbihhZ2UsIG5hLnJtPVQpLAogICAgICAgICAgc2Q9IHNkIChhZ2UsIG5hLnJtPVQpKQpgYGAKCiMjIFBpcGUgT3BlcmF0b3JzCkEgbmV3IGZ1bmN0aW9uOiAqKnBpcGUgb3BlcmF0b3JzKiogYCU+JWAgcGlwZXMgYSB2YWx1ZSBpbnRvIHRoZSBuZXh0IGZ1bmN0aW9uOgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CnJhd19kYXRhICU+JSAKICBzdW1tYXJpc2UoLiwgbWVhbj0gbWVhbihhZ2UsIG5hLnJtPVQpLAogICAgICAgICAgICBzZD0gc2QgKGFnZSwgbmEucm09VCkpCmBgYAoKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQpyYXdfZGF0YSAlPiUgCiAgc3VtbWFyaXNlKG1lYW49IG1lYW4oYWdlLCBuYS5ybT1UKSwKICAgICAgICAgICAgc2Q9IHNkIChhZ2UsIG5hLnJtPVQpKQpgYGAKCkNhbGN1bGF0ZSB0aGUgYWdlIG1lYW4gb2YgeW91bmdlciB0aGFuIDI1IHBhcnRpY2lwYW50cyBvbmx5OgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CnJhd19kYXRhICU+JSAKICBmaWx0ZXIgKGFnZSA8IDI1KSAlPiUKICBzdW1tYXJpc2UobWVhbj0gbWVhbihhZ2UsIG5hLnJtPVQpLAogICAgICAgICAgICBzZD0gc2QgKGFnZSwgbmEucm09VCkpCmBgYAoKIyMgR3JvdXAgQnkKCkNhbGN1bGF0ZSB0aGUgYWdlIG1lYW4gb2YgeW91bmdlciB0aGFuIDI1IHBhcnRpY2lwYW50cyAgZm9yIGVhY2ggZ2VuZGVyIHNlcGFyYXRlbHk6CgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KcmF3X2RhdGEgJT4lIAogIGZpbHRlciAoYWdlIDwgMjUpICU+JQogIGdyb3VwX2J5KGdlbmRlcikgJT4lCiAgc3VtbWFyaXNlKG1lYW49IG1lYW4oYWdlLCBuYS5ybT1UKSwKICAgICAgICAgICAgc2Q9IHNkIChhZ2UsIG5hLnJtPVQpKSAlPiUKICB1bmdyb3VwICgpCmBgYCAgICAgICAgIAoKCiogKkV4ZXJjaXNlKjogQ3JlYXRlIGEgY29sdW1uIHRvIHNob3cgaWYgcGFydGljaXBhbnQgaXMgb2xkZXIgdGhhbiAyMyBvciBub3QgYW5kIHRoZW4gY2FsY3VsYXRlIHNsZWVwIHF1YWxpdHkgKGBzbGVlcF9xdWFsaXR5YCkgbWVhbiBmb3IgZWFjaCBncm91cCBzZXBhcmF0ZWx5OgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KcmF3X2RhdGEgJT4lCiAgbXV0YXRlKGFnZV9ncm91cCA9IGNhc2Vfd2hlbihhZ2UgPiAyMyB+ICJncmVhdGVyIHRoYW4gMjMiLCBUfiAieW91bmdlciB0aGFuIDIzIikpICU+JQogIGdyb3VwX2J5KGFnZV9ncm91cCkgJT4lCiAgc3VtbWFyaXNlKHNsZWVwX3F1YWxpdHkgPSBtZWFuKHNsZWVwX3F1YWxpdHksIG5hLnJtPVQpKQpgYGAgICAgIAoKKiAqRXhlcmNpc2UqOiBBZGQgdGhlIGFueGlldHkgdG90YWwgc2NvcmUgKHN1bSkgdG8gdGhlIGRhdGFmcmFtZSBhbmQgdGhlbiBjb252ZXJ0IHN1YmplY3QgY29sdW1uIHRvIGZhY3RvcjoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CmFueGlldHlfZGF0YSA8LSByYXdfZGF0YSAlPiUKICBtdXRhdGUoYW54aWV0eV90b3RhbD0gYW54aWV0eTErYW54aWV0eTIrYW54aWV0eTMrYW54aWV0eTQrYW54aWV0eTUrYW54aWV0eTYrYW54aWV0eTcrYW54aWV0eTgpICU+JQogIG11dGF0ZShzdWJqZWN0PSBmYWN0b3Ioc3ViamVjdCkpCmBgYCAKCiMjIFBpdm90aW5nCgpOZXh0LCB3ZSB3YW50IHRvIHBpdm90IG91ciBkYXRhIHRvIHN3aXRjaCBiZXR3ZWVuIGxvbmcgYW5kIHdpZGUgZm9ybWF0OgoKYGBge3IgZWNobz1GQUxTRSwgb3V0LndpZHRoPSI3MDBweCIsIG91dC5oZWlnaHQ9IjM1MHB4IiwgZmlnLmNhcD0gIkFydHdvcmsgYnkgQWxsaXNvbiBIb3JzdDogaHR0cHM6Ly9naXRodWIuY29tL2FsbGlzb25ob3JzdC9zdGF0cy1pbGx1c3RyYXRpb25zIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZSgnaW5wdXRzJywndGlkeWRhdGFfMS5qcGcnKSkKYGBgCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KCiMgTWFrZSB5b3UgZGF0YSBsb25nCmxvbmdfZGF0YSA8LSByYXdfZGF0YSAlPiUKICBzZWxlY3Qoc3ViamVjdCwgc3RhZ2UxX2NidDpzdGFnZTVfY2J0LHN0YWdlMV9keW5hbWljOnN0YWdlNV9keW5hbWljKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IGMoc3RhZ2UxX2NidDpzdGFnZTVfZHluYW1pYyksIG5hbWVzX3RvID0gJ3N0YWdlJywgdmFsdWVzX3RvID0gJ2RlcHJlc3Npb25fc2NvcmUnKQoKIyBNYWtlIHlvdSBkYXRhIHdpZGUKd2lkZV9kYXRhIDwtIGxvbmdfZGF0YSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gc3RhZ2UsIHZhbHVlc19mcm9tPSBkZXByZXNzaW9uX3Njb3JlKQoKYGBgCgoqICpFeGVyY2lzZSo6IENvbnZlcnQgdGhlIFVOSUNFRiBkYXRhc2V0IHRvIGxvbmcgYW5kIHdpZGUgZm9ybWF0czoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CnVuaWNlZl9kYXRhIDwtIHJlYWRfY3N2KGhlcmUoImNsZWFuZWRfZGF0YSIsInVuaWNlZl91NW1yLmNzdiIpKQoKbGlicmFyeShqYW5pdG9yKQp1bmljZWZfZGF0YV9jbGVhbmVkIDwtIHVuaWNlZl9kYXRhICU+JQogIGNsZWFuX25hbWVzKCkKCnVuaWNlZl9sb25nX2RhdGEgPC0gdW5pY2VmX2RhdGFfY2xlYW5lZCAlPiUgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKHU1bXJfMTk1MDp1NW1yXzIwMTUpLCBuYW1lc190byA9ICd5ZWFyJywgdmFsdWVzX3RvID0gJ3U1bXInKQp1bmljZWZfd2lkZWdfZGF0YSA8LSB1bmljZWZfbG9uZ19kYXRhICU+JSBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gJ3llYXInLCB2YWx1ZXNfZnJvbSA9ICd1NW1yJykKYGBgCgoqTm90ZSo6IFRoZSBjb2RlcyBmb3IgdGhlIHByZXZpb3VzIGV4ZXJjaXNlIHdlcmUgdGFrZW4gZnJvbSBbdGhpcyBibG9nIHBvc3RdKGh0dHBzOi8vc2VqZGVteXIuZ2l0aHViLmlvL3ItdHV0b3JpYWxzL2Jhc2ljcy93aWRlLWFuZC1sb25nLykgd3JpdHRlbiBieSBTaW1vbiBFamRlbXlyLgoKTm93LCBsZXQncyBkbyBzb21lIGNsZWFuaW5nIHVzaW5nIGBkcGx5cmAsIGB0aWR5cmAgYW5kIG90aGVyIGB0aWR5dmVyc2VgIGxpYnJhcmllczogCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBldmFsPUZ9CmNsZWFuZWRfZGF0YSA8LSByYXdfZGF0YSAlPiUgCiAgZmlsdGVyKHByb2dyZXNzID09IDEwMCkgJT4lICMgZmlsdGVyIG91dCB1bmZpbmlzaGVkIHBhcnRpY2lwYW50cwogIHNlbGVjdCgtY29uc2VudF9mb3JtKSAlPiUgI3JlbW92ZSBzb21lIHVzZWxlc3MgY29sdW1ucwogICMgY3JlYXRlIGEgdG90YWwgc2NvcmUgZm9yIG91ciBxdWVzdGlvbm5haXJlCiAgbXV0YXRlKGFueGlldHlfdG90YWw9IGFueGlldHkxK2FueGlldHkyK2FueGlldHkzK2FueGlldHk0K2FueGlldHk1K2FueGlldHk2K2FueGlldHk3K2FueGlldHk4KSAlPiUKICBzZWxlY3QoLWFueGlldHkxOi1hbnhpZXR5OCkgJT4lCiAgIyBtYWtlIG91ciBkYXRhZnJhbWUgbG9uZwogIHBpdm90X2xvbmdlcihjb2xzID0gYyhzdGFnZTFfY2J0OnN0YWdlNV9jYnQsc3RhZ2UxX2R5bmFtaWM6c3RhZ2U1X2R5bmFtaWMpLG5hbWVzX3RvID0gJ3N0YWdlJyx2YWx1ZXNfdG8gPSAnZGVwcmVzc2lvbl9zY29yZScpICU+JSAKICAjcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHN0YWdlLCB2YWx1ZXNfZnJvbT0gZGVwcmVzc2lvbl9zY29yZSkgIyB0aGlzIGNvZGUgY2hhbmdlIG91ciBkYXRhZnJhbWUgYmFjayB0byB3aWRlCiAgZmlsdGVyKCFpcy5uYShkZXByZXNzaW9uX3Njb3JlKSkgJT4lICNyZW1vdmUgcm93cyB3aXRoIGRlcHJlc3Npb25fc2NvcmUgPT0gTkEKICBtdXRhdGUoc3RhZ2U9IGdzdWIoIl8uKiIsICIiLCBzdGFnZSkpICU+JQogIHNlbGVjdCAoc3ViamVjdCwgYWdlLCBnZW5kZXIsIGdyb3VwLCBzdGFnZSwgZGVwcmVzc2lvbl9zY29yZSwgYW54aWV0eV90b3RhbCwgc2xlZXBfcXVhbGl0eSwgbGlmZV9zYXRpc2ZhY3Rpb24pCmBgYAoKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBlY2hvPUZ9CmNsZWFuZWRfZGF0YSA8LSByYXdfZGF0YSAlPiUgCiAgZmlsdGVyKHByb2dyZXNzID09IDEwMCkgJT4lICMgZmlsdGVyIG91dCB1bmZpbmlzaGVkIHBhcnRpY2lwYW50cwogIHNlbGVjdCgtY29uc2VudF9mb3JtKSAlPiUgI3JlbW92ZSBzb21lIHVzZWxlc3MgY29sdW1ucwogICMgY3JlYXRlIGEgdG90YWwgc2NvcmUgZm9yIG91ciBxdWVzdGlvbm5haXJlCiAgbXV0YXRlKGFueGlldHlfdG90YWw9IGFueGlldHkxK2FueGlldHkyK2FueGlldHkzK2FueGlldHk0K2FueGlldHk1K2FueGlldHk2K2FueGlldHk3K2FueGlldHk4KSAlPiUKICBzZWxlY3QoLWFueGlldHkxOi1hbnhpZXR5OCkgJT4lCiAgIyBtYWtlIG91ciBkYXRhZnJhbWUgbG9uZwogIHBpdm90X2xvbmdlcihjb2xzID0gYyhzdGFnZTFfY2J0OnN0YWdlNV9jYnQsc3RhZ2UxX2R5bmFtaWM6c3RhZ2U1X2R5bmFtaWMpLG5hbWVzX3RvID0gJ3N0YWdlJyx2YWx1ZXNfdG8gPSAnZGVwcmVzc2lvbl9zY29yZScpICU+JSAKICAjcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHN0YWdlLCB2YWx1ZXNfZnJvbT0gZGVwcmVzc2lvbl9zY29yZSkgIyB0aGlzIGNvZGUgY2hhbmdlIG91ciBkYXRhZnJhbWUgYmFjayB0byB3aWRlCiAgZmlsdGVyKCFpcy5uYShkZXByZXNzaW9uX3Njb3JlKSkgJT4lICNyZW1vdmUgcm93cyB3aXRoIGRlcHJlc3Npb25fc2NvcmUgPT0gTkEKICBtdXRhdGUoc3RhZ2U9IGdzdWIoIl8uKiIsICIiLCBzdGFnZSkpICU+JQogIHNlbGVjdCAoc3ViamVjdCwgYWdlLCBnZW5kZXIsIGdyb3VwLCBzdGFnZSwgZGVwcmVzc2lvbl9zY29yZSwgYW54aWV0eV90b3RhbCwgc2xlZXBfcXVhbGl0eSwgbGlmZV9zYXRpc2ZhY3Rpb24pCgprbml0cjo6a2FibGUoaGVhZChjbGVhbmVkX2RhdGEpKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJib3JkZXJlZCIsICJjb25kZW5zZWQiKSwgZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gRiklPiUKICBzY3JvbGxfYm94KHdpZHRoID0gIjc4MHB4IikKYGBgCgpPaywgbm93IHRoZSBkYXRhIGlzIGNsZWFuIGFuZCB0aWR5IHdoaWNoIG1lYW5zOgoKPiAxLiBFYWNoIHZhcmlhYmxlIGZvcm1zIGEgY29sdW1uLgoyLiBFYWNoIG9ic2VydmF0aW9uIGZvcm1zIGEgcm93LgozLiBFYWNoIHR5cGUgb2Ygb2JzZXJ2YXRpb25hbCB1bml0IGZvcm1zIGEgdGFibGUgKFtXaWNraGFtXShodHRwczovL3ZpdGEuaGFkLmNvLm56L3BhcGVycy90aWR5LWRhdGEucGRmKSwgMjAxNCkuCgoKQ2hlY2sgdGhlIGRhdGFmcmFtZSBhbmQgYWxsIHRoZSBkYXRhIHR5cGVzOgpgYGB7cn0Kc3RyKGNsZWFuZWRfZGF0YSkKYGBgCgpGaW5hbGx5LCB3ZSBzYXZlIG91ciBkYXRhIHRvIHRoZSBgY2xlYW5lZF9kYXRhYCBmb2xkZXIuCgpgYGB7cn0Kd3JpdGVfY3N2KGNsZWFuZWRfZGF0YSwgaGVyZSgiY2xlYW5lZF9kYXRhIiwiY2xlYW5lZF9kYXRhX2V4cDEuY3N2IikpCmBgYAoKCiMgRGF0YSBWaXN1YWxpemF0aW9uCgpgYGB7ciBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjcwMHB4Iiwgb3V0LmhlaWdodD0iMzUwcHgiLCBmaWcuY2FwPSAiQXJ0d29yayBieSBBbGxpc29uIEhvcnN0OiBodHRwczovL2dpdGh1Yi5jb20vYWxsaXNvbmhvcnN0L3N0YXRzLWlsbHVzdHJhdGlvbnMifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCdpbnB1dHMnLCdnZ3Bsb3QyX21hc3RlcnBpZWNlLnBuZycpKQpgYGAKCkJlZm9yZSBzdGFydGluZyB0aGUgZ2dwbG90LCBsZXQncyB0cnkgYSB2aXN1YWxpemF0aW9uIHVzaW5nIGEgZnVuY3Rpb24gZnJvbSB0aGUgQmFzZSBSIHRoZSBwbG90KCkgZnVuY3Rpb24gc2hvd3MgdGhlIGFzc29jaWF0aW9uIG9mIGVhY2ggdmFyaWFibGUgYWdhaW5zdCB0aGUgb3RoZXIgb25lIGluIGEgZGF0YSBoYW5keSBmb3IgZGF0YSB3aXRoIGZldyBudW1iZXIgb2YgdmFyaWFibGVzIHRvIHNlZSBpZiB0aGVyZSBhcmUgYW55IHBhdHRlcm5zCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTR9CgpleGFtX2RhdGE8LSByZWFkX2NzdihoZXJlOjpoZXJlKCJjbGVhbmVkX2RhdGEiLCAiZXhhbV9kYXRhLmNzdiIpKQoKcGxvdCh4ID0gZXhhbV9kYXRhJEFueGlldHksIHkgPSBleGFtX2RhdGEkRXhhbSkKCmBgYAoKVGhlIGNvZGUgYWxzbyB3b3JrcyB3aXRob3V0IHdyaXRpbmcgeCBhbmQgeSwgaG93ZXZlciwgd3JpdGluZyB0aGVtIGlzIHN0cm9uZ2x5IHJlY29tbWVuZGVkCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9MiwgZmlnLndpZHRoPTR9CgpwbG90KGV4YW1fZGF0YSRBbnhpZXR5LCBleGFtX2RhdGEkRXhhbSkKYGBgCgpgZ2dwbG90YCwgdGhlIGdnIGluIGdncGxvdCBzdGFuZHMgZm9yIGdyYW1tYXIgb2YgZ3JhcGhpY3MuIEdyYW1tYXIgb2YgZ3JhcGhpY3MgYmFzaWNhbGx5IHNheXMgYW55IGdyYXBoaWNhbCByZXByZXNlbnRhdGlvbiBvZiBkYXRhLCBjYW4gYmUgcHJvZHVjZWQgYnkgYSBzZXJpZXMgb2YgbGF5ZXJzLiBZb3UgY2FuIHRoaW5rIG9mIGEgbGF5ZXIgYXMgYSBwbGFzdGljIHRyYW5zcGFyZW5jeS4gTGV0cyBkcmF3IHRoZSBzYW1lIHBsb3QgdXNpbmcgZ2dwbG90LiBBbHdheXMsIG1lbnRpb24gdGhlIGRhdGEgd2UgYXJlIGdvaW5nIHRvIHdvcmsgd2l0aC4KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGRwaT0gMzAwLCBmaWcuaGVpZ2h0PTIsIGZpZy53aWR0aD00fQpnZ3Bsb3QoZGF0YSA9IGV4YW1fZGF0YSwgYWVzKHggPSBFeGFtLCB5ID0gQW54aWV0eSkpCmBgYAoKCi0gYGFlc2A6IGFlcyB3aGljaCBzdGFuZHMgZm9yIGFlc3RoZXRpY3MgaXMgYSByZWxhdGlvbnNoaXAgYmV0d2VlbiBhIHZhcmlhYmxlIGluIHlvdXIgZGF0YXNldCBhbmQgYW4gYXNwZWN0IG9mIHRoZSBwbG90IHRoYXQgaXMgZ29pbmcgdG8gdmlzdWFsbHkgY29udmV5IHRoZSBpbmZvcm1hdGlvbiB0byB0aGUgcmVhZGVyCgotIFZpc3VhbCBlbGVtZW50cyBhcmUga25vd24gYXMgZ2VvbXMgKHNob3J0IGZvciAnZ2VvbWV0cmljIG9iamVjdHMnKSBpbiBnZ3Bsb3QgMi4gV2hlbiB3ZSBkZWZpbmUgYSBsYXllciwgd2UgaGF2ZSB0byB0ZWxsIFIgd2hhdCBnZW9tIHdlIHdhbnQgZGlzcGxheWVkIG9uIHRoYXQgbGF5ZXIgKGRvIHdlIHdhbnQgYSBiYXIsIGxpbmUgZG90LCBldGMuPykKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gRXhhbSwgeSA9IEFueGlldHkpKSsgZ2VvbV9wb2ludCgpCmBgYAoKU28sIGxldHMgdHJ5IHNvbWUgb2YgdGhlbSBoZXJlIGxpa2Ugc2hhcGUgYW5kIHNpemUuIEJlIGNhcmVmdWwgd2l0aCB0aGUgKyBzaWduLCBpZiB5b3UgY2xpbmsgZW50ZXIgZm9yIHRoZSBuZXh0IHBhcnQgb2YgdGhlIGNvZGUsIHRoZSArIHNpZ24gc2hvdWxkIG5vdCBnbyB0byB0aGUgbmV4dCBsaW5lCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9MiwgZmlnLndpZHRoPTR9CmdncGxvdChkYXRhID0gZXhhbV9kYXRhLCBhZXMoeCA9IEV4YW0sIHkgPSBBbnhpZXR5KSkrCiAgZ2VvbV9wb2ludChzaXplID0gMiwgc2hhcGUgPSA4KQpgYGAKClRoZSBjdXJyZW50IHBsb3QgaXMgbm90IHZlcnkgaW5mb3JtYXRpdmUgYWJvdXQgdGhlIHBhdHRlcm5zIGZvciBlYWNoIGdlbmRlci4KYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGRwaT0gMzAwLCBmaWcuaGVpZ2h0PTIsIGZpZy53aWR0aD00fQpnZ3Bsb3QoZGF0YSA9IGV4YW1fZGF0YSwgYWVzKHggPSBFeGFtLCB5ID0gQW54aWV0eSwgY29sb3IgPSBHZW5kZXIpKSsKICBnZW9tX3BvaW50KHNpemUgPSAyLCBzaGFwZSA9IDEwKQoKZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gRXhhbSwgeSA9IEFueGlldHksIGNvbG9yID0gR2VuZGVyLCBzaGFwZSA9IEdlbmRlcikpKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIsIHNoYXBlID0gMTApCmBgYAoKUXVlc3Rpb246IHdoeSB0aGUgYWJvdmUgY29kZSBkb2Vzbid0IG1ha2UgYW55IGNoYW5nZT8KCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gRXhhbSwgeSA9IEFueGlldHksIGNvbG9yID0gR2VuZGVyLCBzaGFwZSA9IEdlbmRlcikpKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIpCmBgYAoKQ2FuIGFzc2lnbiB0aGUgZmlyc3QgbGF5ZXIgdG8gYSB2YXJpYWJsZSB0byByZWR1Y2UgdGhlIGxlbmd0aCBvZiBjb2RlcyBmb3IgbmV4dCBsYXllcnMuCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9MiwgZmlnLndpZHRoPTR9Ck15X2dyYXBoIDwtIGdncGxvdChkYXRhID0gZXhhbV9kYXRhLCBhZXMoeCA9IEV4YW0sIHkgPSBBbnhpZXR5KSkKCk15X2dyYXBoICsgZ2VvbV9wb2ludCgpCmBgYAoKbGV0cyBhZGQgYSBsaW5lIHRvIHRoZSBjdXJyZW50IGdyYXBoCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KTXlfZ3JhcGggKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpCmBgYAoKQWVzdGhldGljcyBjYW4gYmUgc2V0IGZvciBhbGwgbGF5ZXJzIG9mIHRoZSBwbG90IChpLmUuLCBkZWZpbmVkIGluIHRoZSBwbG90IGFzIGEgd2hvbGUpIG9yIGNhbiBiZSBzZXQgaW5kaXZpZHVhbGx5IGZvciBlYWNoIGdlb20gaW4gYSBwbG90LgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGRwaT0gMzAwLCBmaWcuaGVpZ2h0PTIsIGZpZy53aWR0aD00fQpNeV9ncmFwaCArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gR2VuZGVyKSkgKyBnZW9tX3Ntb290aCgpCgpNeV9ncmFwaCArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gR2VuZGVyKSkgKyBnZW9tX3Ntb290aChhZXMoY29sb3IgPSBHZW5kZXIpKQpgYGAKClRoZSBzaGFkZWQgYXJlYSBhcm91bmQgdGhlIGxpbmUgaXMgdGhlIDk1JSBjb25maWRlbmNlIGludGVydmFsIGFyb3VuZCB0aGUgbGluZS4gV2UgY2FuIHN3aXRjaCB0aGlzIG9mZiBieSAgYWRkaW5nIGBzZSA9IEZgICh3aGljaCBpcyBzaG9ydCBmb3IgJ3N0YW5kYXJkIGVycm9yID0gRmFsc2UnKQoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGRwaT0gMzAwLCBmaWcuaGVpZ2h0PTIsIGZpZy53aWR0aD00fQpNeV9ncmFwaCArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKHNlID0gRikKYGBgCgoKV2hhdCBpZiB3ZSB3YW50IG91ciBsaW5lIHRvIGJlIGEgZGlyZWN0IGxpbmU/CmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KTXlfZ3JhcGggKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChzZSA9IEYsIG1ldGhvZCA9IGxtKQoKYGBgCkhvdyB0byBjaGFuZ2UgdGhlIGxhYmVscyBvZiB4IGFuZCB5IGF4ZXM/CmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KTXlfZ3JhcGggKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChzZSA9IEYsIG1ldGhvZCA9IGxtKSArCiAgbGFicyh4ID0gIkV4YW0gc2NvcmVzICUiLCB5ID0gIkFueGlldHkgc2NvcmVzIikKYGBgCgpIaXN0b2dyYW1zIGFyZSB1c2VkIHRvIHNob3cgZGlzdHJpYnV0aW9ucyBvZiB2YXJpYWJsZXMgd2hpbGUgYmFyIGNoYXJ0cyBhcmUgdXNlZCB0byBjb21wYXJlIHZhcmlhYmxlcy4gSGlzdG9ncmFtcyBwbG90IHF1YW50aXRhdGl2ZSBkYXRhIHdpdGggcmFuZ2VzIG9mIHRoZSBkYXRhIGdyb3VwZWQgaW50byBiaW5zIG9yIGludGVydmFscyB3aGlsZSBiYXIgY2hhcnRzIHBsb3QgY2F0ZWdvcmljYWwgZGF0YS4KCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KI2dncGxvdChkYXRhID0gZXhhbV9kYXRhLCBhZXMoeCA9IEFueGlldHksIHkgPSBFeGFtICkpICsgZ2VvbV9oaXN0b2dyYW0oKQojIHRoZSBjb2RlIGFib3ZlIGdpdmVzIGFuIGVycm9yIGFzIGdlb21faGlzdG9ncmFtIGNhbiBvbmx5IGhhdmUgeCBvciB5IGF4aXMgaW4gaXRzIGFlcygpCgpnZ3Bsb3QoZGF0YSA9IGV4YW1fZGF0YSwgYWVzKHggPSBBbnhpZXR5KSkgKyBnZW9tX2hpc3RvZ3JhbSgpCgpnZ3Bsb3QoZGF0YSA9IGV4YW1fZGF0YSwgYWVzKHkgPSBBbnhpZXR5KSkgKyBnZW9tX2hpc3RvZ3JhbSgpCgpnZ3Bsb3QoZGF0YSA9IGV4YW1fZGF0YSwgYWVzKHggPSBBbnhpZXR5KSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzEpCgpnZ3Bsb3QoZGF0YSA9IGV4YW1fZGF0YSwgYWVzKHggPSBBbnhpZXR5KSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzEsIGZpbGwgPSAiZ3JlZW4iKQoKZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gQW54aWV0eSkpICsgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDMxLCBmaWxsID0gImdyZWVuIiwgY29sID0gInJlZCIpCmBgYAoKTGV0J3Mgc3RvcCB1c2luZyB0aGUgTXlfZ3JhcGggdmFyaWFibGUgYW5kIHdyaXRlIHRoZSB3aG9sZSBjb2RlIGZyb20gdGhlIHN0YXJ0IGFnYWluIGZvciBhIGJhciBjaGFydApgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9MiwgZmlnLndpZHRoPTR9CmdncGxvdChkYXRhID0gZXhhbV9kYXRhLCBhZXMoeCA9IFNsZWVwX3F1YWxpdHkpKSsKICBnZW9tX2JhcigpCmBgYApCZWNhdXNlIHdlIHdhbnQgdG8gcGxvdCBhIHN1bW1hcnkgb2YgdGhlIGRhdGEgKHRoZSBtZWFuKSByYXRoZXIgdGhhbiB0aGUgcmF3IHNjb3JlcyB0aGVtc2VsdmVzLCB3ZSBoYXZlIHRvIHVzZSBhIHN0YXQuCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gU2xlZXBfcXVhbGl0eSwgeSA9IEV4YW0sIGZpbGwgPSBHZW5kZXIpKSsKICBnZW9tX2JhcihzdGF0ID0gInN1bW1hcnkiLCBmdW4gPSAibWVhbiIpCgoKZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gU2xlZXBfcXVhbGl0eSwgeSA9IEV4YW0sIGZpbGwgPSBHZW5kZXIpKSsKICBnZW9tX2JhcihzdGF0ID0gInN1bW1hcnkiLCBmdW4gPSAibWVhbiIsIHBvc2l0aW9uID0gImRvZGdlIikKYGBgCgpUaGUgb3RoZXIgd2F5IHRvIGdldCB0aGUgc2FtZSBwbG90IHRoYXQgdGhlIGNvZGUgYWJvdmUgZ2l2ZXMsIGlzIHVzaW5nIHRoZSBzdGF0X3N1bW1hcnkgZnVuY3Rpb24gdGhhdCB0YWtlcyB0aGUgZm9sbG93aW5nIGdlbmVyYWwgZm9ybTogYHN0YXRfc3VtbWFyeShmdW5jdGlvbiA9IHgsIGdlb20gPSB5KWAKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9NH0KZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gU2xlZXBfcXVhbGl0eSwgeSA9IEV4YW0sIGZpbGwgPSBHZW5kZXIpKSsKICBzdGF0X3N1bW1hcnkoZnVuID0gbWVhbiwgZ2VvbSA9ICJiYXIiLCBwb3NpdGlvbiA9ICJkb2RnZSIpCmBgYAoKSG93IHRvIGNvbWJpbmUgbXVsdGlwbGUgcGxvdHM/IEhvdyB0byBjb21iaW5lIG11bHRpcGxlIHBsb3RzPyBXZSBjYW4gdXNlIHRoZSBgcGF0Y2h3b3JrYCBwYWNrYWdlLiBBIG5pY2UgdHV0b3JpYWwgb24gdXNpbmcgdGhpcyBwYWNrYWdlIGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cHM6Ly9wYXRjaHdvcmsuZGF0YS1pbWFnaW5pc3QuY29tL2FydGljbGVzL3BhdGNod29yay5odG1sKQoKCmBgYHtyIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNzAwcHgiLCBvdXQuaGVpZ2h0PSIzNTBweCIsIGZpZy5jYXA9ICJBcnR3b3JrIGJ5IEFsbGlzb24gSG9yc3Q6IGh0dHBzOi8vZ2l0aHViLmNvbS9hbGxpc29uaG9yc3Qvc3RhdHMtaWxsdXN0cmF0aW9ucyJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmUoJ2lucHV0cycsJ3BhdGNod29ya18xLmpwZycpKQpgYGAKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GLCBkcGk9IDMwMCwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9Nn0KcDEgPSBNeV9ncmFwaCArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gR2VuZGVyKSkgKyBnZW9tX3Ntb290aCgpCgpwMiA9IGdncGxvdChkYXRhID0gZXhhbV9kYXRhLCBhZXMoeCA9IEFueGlldHkpKSArIGdlb21faGlzdG9ncmFtKGJpbnMgPSAzMSkKCnAzID0gZ2dwbG90KGRhdGEgPSBleGFtX2RhdGEsIGFlcyh4ID0gU2xlZXBfcXVhbGl0eSwgeSA9IEV4YW0sIGZpbGwgPSBHZW5kZXIpKSsKICBzdGF0X3N1bW1hcnkoZnVuID0gbWVhbiwgZ2VvbSA9ICJiYXIiLCBwb3NpdGlvbiA9ICJkb2RnZSIpCgpwNCA9IE15X2dyYXBoICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoc2UgPSBGLCBtZXRob2QgPSBsbSkgKwogIGxhYnMoeCA9ICJFeGFtIHNjb3JlcyAlIiwgeSA9ICJBbnhpZXR5IHNjb3JlcyIpCgpjb21iaW5lZCA9IHAxICsgcDIrIHAzICsgcDQgKyBwbG90X2xheW91dChucm93ID0gNCwgYnlyb3cgPSBGKQoKY29tYmluZWQKCnAxIHwgcDIgLyBwMyAvIHA0CgpwMSB8IHAyIC8gKHAzIC8gcDQpCmBgYAoKCmBnZ3NhdmUoKWAgZnVuY3Rpb24sIHdoaWNoIGlzIGEgdmVyc2F0aWxlIGV4cG9ydGluZyBmdW5jdGlvbiB0aGF0IGNhbiBleHBvcnQgYXMgUG9zdFNjcmlwdCAoLmVwcy8ucHMpLCB0ZXggKHBpY3RleCksIHBkZiwganBlZywgdGlmZiwgcG5nLCBibXAsIHN2ZyBhbmQgd21mIChpbiBXaW5kb3dzIG9ubHkpLiBJbiBpdHMgYmFzaWMgZm9ybSwgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZnVuY3Rpb24gaXMgdmVyeSBzaW1wbGU6IGBnZ3NhdmUoZmlsZW5hbWUpYAoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGRwaT0gMzAwLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD01fQpnZ3NhdmUoY29tYmluZWQsIGZpbGVuYW1lID0gaGVyZSgib3V0cHV0cyIsICJjb21iaW5lZC5wbmciKSwgZHBpPTMwMCkKYGBgCgoKTm93IHRoYXQgd2UgbGVhcm5lZCB0aGUgYmFzaWNzIG9mIGdncGxvdCwgbGV0J3MgZHJhdyBzb21lIHBsb3QgZm9yIG91ciBleHBlcmltZW50IGRhdGEuIEZpcnN0LCB3ZSBuZWVkIHRvIGNyZWF0ZSBhIGRhdGFzZXQgd2l0aCBhZ2dyZWdhdGVkIGBkZXByZXNzaW9uX3Njb3JlYCBzY29yZXMgb3ZlciBgZ3JvdXBgIGFuZCBgc3RhZ2VgLiBXZSB3aWxsIHVzZSB0aGlzIGRhdGFzZXQgZm9yIGxpbmUgYW5kIGJhciBncmFwaHMuCgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTV9CmxpYnJhcnkoZ2dzY2kpCgpkYXRhX2V4cDFfb3JpZyA8LSByZWFkX2NzdihoZXJlKCJjbGVhbmVkX2RhdGEiLCJjbGVhbmVkX2RhdGFfZXhwMS5jc3YiKSkKCmRhdGFfZXhwMSA8LSBkYXRhX2V4cDFfb3JpZyU+JSAKICAjbXV0YXRlX2lmKGlzLmNoYXJhY3RlciwgZmFjdG9yKSAlPiUKICBtdXRhdGUoc3ViamVjdD0gZmFjdG9yKHN1YmplY3QpLCAjIGNvbnZlcnQgYWxsIGNoYXJhY3RlcnMgdG8gZmFjdG9yCiAgICAgICAgIGdyb3VwID0gZmFjdG9yKGdyb3VwKSwKICAgICAgICAgc3RhZ2UgPSBmYWN0b3Ioc3RhZ2UpKQoKCmFnZ3JlZ2F0ZWRfZGF0YV9leHAxIDwtIGRhdGFfZXhwMSAlPiUKICBncm91cF9ieShzdGFnZSwgZ3JvdXApICU+JQogIG11dGF0ZShkZXByZXNzaW9uX3Njb3JlID0gbWVhbihkZXByZXNzaW9uX3Njb3JlKSkgJT4lCiAgdW5ncm91cCgpCgoKYmFycGxvdF9leHAxIDwtIGFnZ3JlZ2F0ZWRfZGF0YV9leHAxICU+JQogIGdncGxvdChhZXMoeD1zdGFnZSwgeT0gZGVwcmVzc2lvbl9zY29yZSwgZmlsbD1ncm91cCkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb249ICJkb2RnZSIpKwogIGxhYnMgKHg9ICcnLCB5PSAiRGVwcmVzc2lvbiBTY29yZSIpICsgCiAgdGhlbWVfYncoKSArIAogIHNjYWxlX2ZpbGxfamFtYSgpIAoKI2dnc2F2ZShiYXJwbG90X2V4cDEsIGZpbGVuYW1lID0gaGVyZSgib3V0cHV0cyIsImJhcnBsb3RfZXhwMS5wbmciKSwgZHBpPTMwMCkKCgpiYXJwbG90X2ZhY2V0X2V4cDEgPC0gYWdncmVnYXRlZF9kYXRhX2V4cDEgJT4lCiAgZ2dwbG90KGFlcyh4PWdyb3VwLCB5PSBkZXByZXNzaW9uX3Njb3JlLCBmaWxsPXN0YWdlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbj0gImRvZGdlIikrCiAgbGFicyAoeD0gJycsIHk9ICJEZXByZXNzaW9uIFNjb3JlIikgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMSksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpKSArCiAgZmFjZXRfd3JhcCh+c3RhZ2UsIG5yb3cgPSAxKSsKICBzY2FsZV9maWxsX2pjbygpIAoKI2dnc2F2ZShiYXJwbG90X2ZhY2V0X2V4cDEsIGZpbGVuYW1lID0gaGVyZSgib3V0cHV0cyIsImJhcnBsb3RfZmFjZXRfZXhwMS5wbmciKSwgZHBpPTMwMCkKCgpsaW5lcGxvdF9leHAxIDwtIGFnZ3JlZ2F0ZWRfZGF0YV9leHAxICU+JQogIGdncGxvdChhZXMoeD1mYWN0b3Ioc3RhZ2UpLCB5PSBkZXByZXNzaW9uX3Njb3JlLCBncm91cD0gZ3JvdXAsIGNvbG9yPSBncm91cCkpICsKICBnZW9tX2xpbmUoYWVzKGxpbmV0eXBlPSBncm91cCkpICsKICBnZW9tX3BvaW50KHNpemU9IDUpKwogIGxhYnMgKHg9ICcnLCB5PSAiRGVwcmVzc2lvbiBTY29yZSIpICsgCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTEpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSkgKwogIHNjYWxlX2NvbG9yX25lam0oKSAKCiNnZ3NhdmUobGluZXBsb3RfZXhwMSwgZmlsZW5hbWUgPSBoZXJlKCJvdXRwdXRzIiwibGluZXBsb3RfZXhwMS5wbmciKSwgZHBpPTMwMCkKCgp2aW9saW5wbG90X2V4cDEgPC0gZGF0YV9leHAxICU+JQogIGdncGxvdChhZXMoeD1mYWN0b3Ioc3RhZ2UpLCB5PSBkZXByZXNzaW9uX3Njb3JlLCBmaWxsPSBncm91cCkpICsKICBnZW9tX3Zpb2xpbigpKwogIGxhYnMgKHg9ICcnLCB5PSAiRGVwcmVzc2lvbiBTY29yZSIpICsgCiAgdGhlbWVfYncoKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMSkpICsKICBzY2FsZV9maWxsX2QzKCkgCgojZ2dzYXZlKHZpb2xpbnBsb3RfZXhwMSwgZmlsZW5hbWUgPSBoZXJlKCJvdXRwdXRzIiwidmlvbGlucGxvdF9leHAxLnBuZyIpLCBkcGk9MzAwKQoKCmJveHBsb3RfZXhwMSA8LSBkYXRhX2V4cDEgJT4lCiAgZ2dwbG90KGFlcyh4PWZhY3RvcihzdGFnZSksIHk9IGRlcHJlc3Npb25fc2NvcmUsIGZpbGw9IGdyb3VwKSkgKwogIGdlb21fYm94cGxvdCgpKwogICNnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGg9MC43NSksIGFscGhhPSAuNSkrCiAgbGFicyAoeD0gJycsIHk9ICJEZXByZXNzaW9uIFNjb3JlIikgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTExKSkgKwogIHNjYWxlX2ZpbGxfc2ltcHNvbnMoKSAKCiNnZ3NhdmUoYm94cGxvdF9leHAxLCBmaWxlbmFtZSA9IGhlcmUoIm91dHB1dHMiLCJib3hwbG90X2V4cDEucG5nIiksIGRwaT0zMDApCgoKYm94cGxvdF9mYWNldF9leHAxIDwtIGRhdGFfZXhwMSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZmFjdG9yKHN0YWdlKSwgeT0gZGVwcmVzc2lvbl9zY29yZSwgZmlsbD0gZ3JvdXApKSArCiAgZ2VvbV9ib3hwbG90KCkrCiAgbGFicyAoeD0gJycsIHk9ICJEZXByZXNzaW9uIFNjb3JlIikgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTExKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpICsKICBmYWNldF93cmFwKH5ncm91cCkrCiAgc2NhbGVfY29sb3Jfc2ltcHNvbnMoKSAKCiNnZ3NhdmUoYm94cGxvdF9mYWNldF9leHAxLCBmaWxlbmFtZSA9IGhlcmUoIm91dHB1dHMiLCJib3hwbG90X2ZhY2V0X2V4cDEucG5nIiksIGRwaT0zMDApCgpgYGAKCkxldCdzIGNvbWJpbmUgb3VyIHBsb3RzOgoKYGBge3IgZHBpPSAzMDAsIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTl9Cgpjb21iaW5lZF9wbG90X2V4cDEgPC0gYmFycGxvdF9mYWNldF9leHAxIC8gKGxpbmVwbG90X2V4cDErdmlvbGlucGxvdF9leHAxK2JveHBsb3RfZXhwMSkKY29tYmluZWRfcGxvdF9leHAxCmBgYAoKQW5kIGhlcmUsIHdlIHNhdmUgb3VyIHBsb3RzIHRvIHRoZSBgb3V0cHV0c2AgZm9sZGVyLgpgYGB7cm1lc3NhZ2U9Rn0KZ2dzYXZlKGNvbWJpbmVkX3Bsb3RfZXhwMSwgZmlsZW5hbWUgPSBoZXJlKCJvdXRwdXRzIiwiY29tYmluZWRfcGxvdF9leHAxLnBuZyIpLCBkcGk9MzAwLCB3aWR0aCA9IDEyKQpgYGAKCi0gW0EgY29tcGxldGUgZ3VpZGUgdG8gZ2dwbG90XShodHRwczovL3d3dy5jZWRyaWNzY2hlcmVyLmNvbS8yMDE5LzA4LzA1L2EtZ2dwbG90Mi10dXRvcmlhbC1mb3ItYmVhdXRpZnVsLXBsb3R0aW5nLWluLXIvKQoKIyBEZXNjcmlwdGl2ZSBTdGF0aXN0aWNzCgpgYGB7ciBlY2hvPUZBTFNFLCBvdXQud2lkdGg9IjcwMHB4Iiwgb3V0LmhlaWdodD0iMzUwcHgiLCBmaWcuY2FwPSAiQXJ0d29yayBieSBBbGxpc29uIEhvcnN0OiBodHRwczovL2dpdGh1Yi5jb20vYWxsaXNvbmhvcnN0L3N0YXRzLWlsbHVzdHJhdGlvbnMifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCdpbnB1dHMnLCdub3Rfbm9ybWFsLnBuZycpKQpgYGAKCk5vdywgbGV0J3MgZG8gc29tZSBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzLiBOb3csIHdlIGNhbiBvcGVuIGEgbmV3IHNjcmlwdCBjYWxsZWQgYGRhdGFfYW5hbHlzaXMucmAgYW5kIHJlYWQgc29tZSBkYXRhc2V0cy4gVGhlbiB3ZSB1c2UgYHNraW1yYCBwYWNrYWdlIHRvIGRlc2NyaWJlIG91ciBkYXRhLgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUYsfQpuYXJjaXNzaXNtX2RhdGEgPC0gcmVhZF9jc3YoaGVyZSgiY2xlYW5lZF9kYXRhIiwibmFyY2lzc2lzbV9kYXRhLmNzdiIpKQpuYXJjaXNzaXNtX2RhdGEgJT4lIHNraW1yOjpza2ltKCkKYGBgCgoqICpFeGVyY2lzZSo6IE9wZW4gdGhlIGRhdGFzZXQgY2FsbGVkIGB0cmVhdG1lbnRfZGF0YS5jc3ZgIGFuZCBkbyBhIGRlc2NyaXB0aXZlIGFuYWx5c2lzOgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KdHJlYXRtZW50X2RhdGEgPC0gcmVhZF9jc3YoaGVyZSgiY2xlYW5lZF9kYXRhIiwidHJlYXRtZW50X2RhdGEuY3N2IikpCnRyZWF0bWVudF9kYXRhICU+JSBza2ltcjo6c2tpbSgpCmBgYAoKKiAqRXhlcmNpc2UqOiBEbyB0aGUgc2FtZSB0aGluZyBmb3IgdGhlIGBtZW1vcnlfZGF0YS5jc3ZgLgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9Cm1lbW9yeV9kYXRhIDwtIHJlYWRfY3N2KGhlcmUoImNsZWFuZWRfZGF0YSIsIm1lbW9yeV9kYXRhLmNzdiIpKQptZW1vcnlfZGF0YSAlPiUgZ3JvdXBfYnkodGltZSkgJT4lCiAgc2tpbXI6OnNraW0oKQpgYGAKCgoqICpFeGVyY2lzZSo6IEZvciB0aGlzIGV4ZXJjaXNlLCB3ZSB1c2UgYSBkYXRhc2V0IG9mIG9uZSBvZiBteSBvd24gc3R1ZGllcy4gSW4gdGhpcyBzdHVkeSwgd2UgYXNrZWQgcGFydGljaXBhbnRzIHRvIGd1ZXNzIHRoZSBwaHlzaWNhbCBicmlnaHRuZXNzIG9mIHNvbWUgcmVhc29uaW5nIGFyZ3VtZW50cyBhbmQgdGhlbiB3ZSBnYXZlIGEgY29nbml0aXZlIGFiaWxpdHkgdGVzdC4gKFNlZSB0aGUgb3JpZ2luYWwgc3R1ZHkgW2hlcmVdKGh0dHBzOi8vb3NmLmlvL2VieG5mLykpLiBPcGVuIGBnaGFzZW1pX2JyaWdodG5lc3NfZXhwNC5jc3ZgIGZpbGUgYW5kIGFuc3dlciB0byB0aGUgZm9sbG93aW5nIHF1ZXN0aW9uczoKCjEuIEhvdyBtYW55IHBhcnRpY2lwYW50cyBkaWQgd2UgdGVzdCBpbiB0b3RhbD8KMi4gRmluZCBvdXQgaG93IG1hbnkgbWFsZSBhbmQgZmVtYWxlIHdlIHRlc3RlZC4KMy4gQ2FsY3VsYXRlIG1lYW4gYW5kIHNkIGZvciBhZ2UgYW5kIGNvZ25pdGl2ZSBhYmlsaXR5IChgY29nX2FiaWxpdHlgKS4KCgpgYGB7ciB3YXJuaW5nPUYsIG1lc3NhZ2U9Rn0KZ2hhc2VtaV9kYXRhIDwtIHJlYWRfY3N2KGhlcmUoImNsZWFuZWRfZGF0YSIsImdoYXNlbWlfYnJpZ2h0bmVzc19leHA0LmNzdiIpKQoKZ2hhc2VtaV9kYXRhICU+JSBzdW1tYXJpc2UobiA9IG5fZGlzdGluY3QocGFydGljaXBhbnQpKSAjIG51bWJlciBvZiBwYXJ0aWNpcGFudHM6MjAwCgpnaGFzZW1pX2RhdGEgJT4lIGdyb3VwX2J5IChwYXJ0aWNpcGFudCkgJT4lIGZpbHRlciAocm93X251bWJlcigpPT0xKSAlPiUgZ3JvdXBfYnkgKGdlbmRlcikgJT4lIHN1bW1hcmlzZShuPSBuKCkpICU+JSB1bmdyb3VwKCkgIyAxODMgZmVtYWxlLCAxNyBtYWxlCgpnaGFzZW1pX2RhdGEgJT4lIGRwbHlyOjpzZWxlY3QgKGFnZSwgY29nX2FiaWxpdHkpICU+JSBza2ltcjo6c2tpbSgpICMgbWVhbiBhbmQgc2QgZm9yIGFnZSBhbmQgY29nbml0aXZlIGFiaWxpdHkKYGBgCgoKCiMgRGF0YSBBbmFseXNpcwoKIyMgdC10ZXN0CgpOb3csIHdlIHVzZSB0aGUgdHJlYXRtZW50IGRhdGEgdG8gcnVuIHRocmVlIGRpZmZlcmVudCBpbmRlcGVuZGVudCB0LXRlc3RzLiBTdXBwb3NlIHdlIGRpZCBhbiBleHBlcmltZW50IHRvIGNvbXBhcmUgdGhlIGVmZmVjdGl2ZW5lc3Mgb2YgQ0JUIHZzLiBQc3ljaG9keW5hbWljIHRoZXJhcGllcyBpbiBkZWNyZWFzaW5nIGFueGlldHksIGFuZCBkZXByZXNzaW9uIGFuZCBhbHNvIGluIGltcHJvdmluZyBsaWZlIHNhdGlzZmFjdGlvbjoKCmBgYHtyfQojIHQudGVzdCAoaW5kZXApCnQudGVzdChhbnhpZXR5fnRyZWF0bWVudCwgZGF0YT0gdHJlYXRtZW50X2RhdGEpCnQudGVzdChkZXByZXNzaW9ufnRyZWF0bWVudCwgZGF0YT0gdHJlYXRtZW50X2RhdGEpCnQudGVzdChsaWZlX3NhdGlzZmFjdGlvbn50cmVhdG1lbnQsIGRhdGE9IHRyZWF0bWVudF9kYXRhKQpgYGAKCkluIGFub3RoZXIgZXhwZXJpbWVudCwgc3VwcG9zZSB3ZSBoYXZlIGNyZWF0ZWQgYSBtZXRob2QgdG8gYm9vc3QgbWVtb3J5LiBUaGVuLCB3ZSByZWNydWl0IHNvbWUgcGFydGljaXBhbnRzLCBkbyBhIG1lbW9yeSBwcmUtdGVzdCwgaW1wbGVtZW50IHRoZSBtZXRob2QsIGFuZCBkbyBhIG1lbW9yeSBwb3N0LXRlc3QsIE5vdywgd2Ugd2FudCB0byBzZWUgd2hldGhlciBvdXIgbWV0aG9kIGhhdmUgaW1wcm92ZWQgcGFydGljaXBhbnRzJyBtZW1vcnk6IAoKYGBge3J9CiMgdC50ZXN0IChwYWlyZWQpCnQudGVzdChtZW1vcnlfc2NvcmV+dGltZSwgZGF0YT0gbWVtb3J5X2RhdGEsIHBhaXJlZD0gVCkKYGBgCgpOb3cgdGhhdCB3ZSBsZWFybmVkIGFib3V0IHQtdGVzdCwgbGV0J3MgcGVyZm9ybSB0aGlzIHRlc3Qgb24gb3VyIGRhdGFzZXQuIElzIHRoZXJlIGEgZGlmZmVyZW5jZSBiZXR3ZWVuIGdyb3VwcyBhdCB0aGUgZmlyc3Qgc3RhZ2U/IElkZWFsbHksIHdlIHdhbnQgcGFydGljaXBhbnRzJyBkZXByZXNpb24gc2NvcmUgYXQgdGhlIGZpcnN0IHN0YWdlIHRvIGJlIHNpbWlsYXIgZm9yIGJvdGggZ3JvdXBzIGJlY2F1c2Ugd2UgaGF2ZSBub3Qgc3RhcnRlZCBvdXIgdHJlYXRtZW50IHlldC4gUHJldmlvdXMgZ3JhcGhzIHNob3dlZCB1cyB0aGF0IGRlcHJlc3Npb24gc2NvcmVzIG9mIHRoZSBDQlQgYW5kIFBzeWNob2R5bmFtaWMgZ3JvdXBzIGF0IHRoaXMgc3RhZ2UgYXJlIHByZXR0eSBjbG9zZS4gTGV0J3MgdGVzdCB0aGF0IHVzaW5nIGFuICoqaW5kZXBlbmRlbnQgdC10ZXN0KiogKGJlY2F1c2Ugd2UgaGF2ZSAyIGluZGVwZW5kZW50IGdyb3Vwcyk6CgpgYGB7cn0KIyBJcyB0aGVyZSBhIGRpZmZlcmVuY2UgYmV0d2VlbiBncm91cHMgYXQgdGhlIGZpcnN0IHN0YWdlPwpkYXRhX2V4cDEgJT4lIAogIGdyb3VwX2J5KGdyb3VwKSAlPiUgCiAgZmlsdGVyKHN0YWdlPT0nc3RhZ2UxJykgJT4lIAogIHVuZ3JvdXAgKCkgJT4lCiAgdC50ZXN0KGRlcHJlc3Npb25fc2NvcmV+Z3JvdXAsIGRhdGEgPSAuLCBwYWlyZWQ9RkFMU0UpCmBgYAoKTm93LCB3ZSB3b25kZXIgaWYgcHN5Y2hvdGhlcmFweSB0cmVhdG1lbnRzIHdlcmUgZWZmZWN0aXZlIGF0IGFsbCwgcmVnYXJkbGVzcyBvZiB0aGUgdHJlYXRtZW50IG1ldGhvZC4gU28sIHdlIHdvdWxkIGxpa2UgdG8gdGVzdCBpZiBkZXByZXNpb24gc2NvcmUgYXQgdGhlIGZvcnRoIHN0YWdlIGFyZSBsb3dlciB0aGFuIHNjb3JlcyBhdCB0aGUgc3RhZ2UgMj8gU2luY2UgYSBwYWlyIG9mIHNjb3JlIGF0IHN0YWdlIDIgYW5kIHN0YWdlIDQgaXMgY29taW5nIGZyb20gYSBzYW1lIHBlcnNvbiwgd2UgdXNlICoqcGFpcmVkIHQtdGVzdCoqLgoKYGBge3J9CiMgSXMgdGhlcmUgYSBkaWZmZXJlbmNlIGJldHdlZW4gcmF0aW5ncyBvZiBzdGFnZTIgYW5kIHN0YWdlND8KZGF0YV9leHAxICU+JSAKICBmaWx0ZXIoc3RhZ2U9PSdzdGFnZTInIHwgc3RhZ2U9PSdzdGFnZTQnKSAlPiUgCiAgdW5ncm91cCAoKSAlPiUKICB0LnRlc3QoZGVwcmVzc2lvbl9zY29yZX5zdGFnZSwgZGF0YSA9IC4sIHBhaXJlZD1UUlVFKQpgYGAKCgoqICpFeGVyY2lzZSo6IEpvaG4gZXQgYWwuICgyMDE5KSBpbnZlc3RpZ2F0ZWQgdGhlIGNvbnNlcXVlbmNlcyBvZiBiYWNraW5nIGRvd24gKGNoYW5naW5nIG9uZSdzIG1pbmQgaW4gbGlnaHRzIG9mIGV2aWRlbmNlKWFuZCBob3cgb3RoZXIgcGVvcGxlIHZpZXcgc29tZW9uZSB3aG8gY2hhbmdlIHRoZWlyIG1pbmQuIEluIHRoZWlyIHNlY29uZCBleHBlcmltZW50cywgdGhleSBwcmVzZW50ZWQgcGFydGljaXBhbnRzIGVpdGhlciB3aXRoIGEgcGVyc29uIHdobyBjaGFuZ2VzIHRoZWlyIG1pbmQgb3IgYSBwZXJzb24gd2hvIHJlZnVzZXMgdG8gYmFjayBkb3duLiBUaGVuLCB0aGV5IGFza2VkIHBhcnRpY2lwYW50cyB0byByYXRlIGhvdyBpbnRlbGxpZ2VudCBhbmQgY29uZmlkZW50IHRoZSBwZXJzb24gaXMgKFNlZSB0aGUgb3JpZ2luYWwgc3R1ZHkgW2hlcmVdKGh0dHBzOi8vd3d3Lmhicy5lZHUvZmFjdWx0eS9QdWJsaWNhdGlvbiUyMEZpbGVzL0pvaG4lMjBldCUyMGFsJTIwLSUyMHNlbGYtcHJlc2VudGF0aW9uYWwlMjBjb25zZXF1ZW5jZXNfYjg1YjJjNDMtYTViNS00NzRjLTllMmMtZTk4NTNiMTA3MjdlLnBkZikpLiBUaGV5IHJlcG9ydGVkIHRoYXQ6IAoKPiAiUmVsYXRpdmUgdG8gdGhlIGVudHJlcHJlbmV1ciB3aG8gZGlkIG5vdCBiYWNrIGRvd24sIHBhcnRpY2lwYW50cyBqdWRnZWQgdGhlIGVudHJlcHJlbmV1ciB3aG8gYmFja2VkIGRvd24gYXMgbW9yZSBpbnRlbGxpZ2VudCAoTV9iYWNrZWRfZG93bj01LjEzIG91dCBvZiA3LCBTRD0xLjA5OyBNX2RpZF9ub3RfYmFja19kb3duPTMuOTcsIFNEPTEuNTQ7IHQoMjcxLjEyKT3iiJI3LjU5LCBwIDwgLjAwMSkgYnV0IGxlc3MgY29uZmlkZW50IChNX2JhY2tlZF9kb3duPTQuNTAgb3V0IG9mIDcsIFNEPTEuMzY7IE1fZGlkX25vdF9iYWNrX2Rvd249NS42NSwgU0Q9MS4xMDsgdCgyOTEuMDEpPTguMDgsIHAgPCAuMDAxKS4iLgoKT3BlbiB0aGUgYGpvaG5fYmFja2Rvd25fZXhwMi5jc3ZgIGZpbGUgYW5kIHRyeSB0byByZXByb2R1Y2UgdGhlaXIgcmVzdWx0cy4gUnVuIHR3byBzZXBhcmF0ZSBpbmRlcGVuZGVudCB0LXRlc3QsIG9uZSB3aXRoIGBpbnRlbGxpZ2VudGAgYXMgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSBhbmQgb25lIHdpdGggYGNvbmZpZGVudGAgYXMgdGhlIGRlcGVuZGVudCB2YXJpYWJsZS4gRm9yIGJvdGggdC10ZXN0LCB1c2UgYGJhY2tfZG93bmAgYXMgdGhlIGJldHdlZW4tc3ViamVjdCBpbmRlcGVuZGVudCB2YXJpYWJsZS4KCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQpqb2huX2RhdGEgPC0gcmVhZF9jc3YoaGVyZSgiY2xlYW5lZF9kYXRhIiwiam9obl9iYWNrZG93bl9leHAyLmNzdiIpKQoKCnQudGVzdChpbnRlbGxpZ2VudH5iYWNrX2Rvd24sIGRhdGEgPSBqb2huX2RhdGEsIHBhaXJlZD1GQUxTRSkKdC50ZXN0KGNvbmZpZGVudH5iYWNrX2Rvd24sIGRhdGEgPSBqb2huX2RhdGEsIHBhaXJlZD1GQUxTRSkKYGBgCgoKIyMgQW5hbHlzaXMgb2YgVmFyaWFuY2UgKEFOT1ZBKQoKTm93LCBsZXQncyBhbmFseXNpcyBvdXIgbWFpbiBleHBlcmltZW50IGRhdGE6IERvIHBhcnRpY2lwYW50cyBpbiB0aGUgQ0JUIGdyb3VwIHNob3cgYmV0dGVyIG91dGNvbWUgY29tcGFyZWQgdG8gcGFydGljaXBhbnRzIGluIHRoZSBQc3ljaG9keW5hbWljIGdyb3VwPyBTdXBwb3NlIHdlIGJlbGlldmUgdGhhdCBwYXJ0aWNpcGFudHMgc2hvdWxkIHNob3cgbG93ZXIgZGVwcmVzc2lvbiBhZnRlciA1IG9yIDEwIHNlc3Npb25zIG9mIGJvdGggcHN5Y2hvdGhlcmFweSB0cmVhdG1lbnRzIGFuZCB0aGlzIGRlY3JlYXNlIHNob3VsZCBiZSBtb3JlIHByb25vdW5jZWQgZm9yIENCVCB0aGFuIHBzeWNob2R5bmFtaWMgcHN5Y2hvdGhlcmFweS4gSWYgdGhpcyBpcyB0aGUgY2FzZS4gd2UgZXhwZWN0IGFuIGludGVyYWN0aW9uIGluIHRoZSB0cmFkaXRpb25hbCAqKkFuYWx5c2lzIG9mIFZhcmlhbmNlIChBT05WQSkqKiB0ZXN0LgoKYGBge3IgbWVzc2FnZT1GLCB3YXJuaW5nPUZ9CmFvdl9tMSA8LSBhb3ZfY2FyIChkZXByZXNzaW9uX3Njb3JlIH4gZ3JvdXAqc3RhZ2UgKwogICAgICAgICAgICAgICAgICAgICBFcnJvcihzdWJqZWN0L3N0YWdlKSwgZGF0YSA9IGRhdGFfZXhwMSkgIApgYGAKCmBgYHtyIGVjaG89Rn0Ka25pdHI6OmthYmxlKG5pY2UoYW92X20xKSkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiYm9yZGVyZWQiLCAiY29uZGVuc2VkIiksIGZpeGVkX3RoZWFkID0gVCwgZnVsbF93aWR0aCA9IFQpCmBgYAoKQXMgeW91IGNhbiBzZWUsIHdlIGZvdW5kIGEgc2lnbmlmaWNhbnQgbWFpbiBlZmZlY3Qgb2Ygc3RhZ2UgYW5kIGEgc2lnbmlmaWNhbnQgZ3JvdXAgYnkgc3RhZ2UgaW50ZXJhY3Rpb24uIFdlIGNhbiB1c2UgdGhlIGBlbW1lYW5zYCBwYWNrYWdlIHRvIGRvIHBvc3QtaG9jIHRlc3RzLgoKYGBge3Igd2FybmluZz1GLCBtZXNzYWdlPUZ9CiMgbWFpbiBlZmZlY3Qgb2Ygc3RhZ2UKZW1tZWFucyhhb3ZfbTEsICdzdGFnZScpCnBhaXJzKGVtbWVhbnMoYW92X20xLCAnc3RhZ2UnKSwgYWRqdXN0PSAnaG9sbScpCmBgYAoKCmBgYHtyIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQojIGdyb3VwIGJ5IHN0YWdlIGludGVyYWN0aW9uCmVtbWVhbnMoYW92X20xLCAiZ3JvdXAiLCBieT0gInN0YWdlIikKdXBkYXRlKHBhaXJzKGVtbWVhbnMoYW92X20xLCAiZ3JvdXAiLCBieT0gInN0YWdlIikpLCBieSA9IE5VTEwsIGFkanVzdCA9ICJob2xtIikgCmBgYAoKWW91IGNhbiB1c2UgdGhlIGBhZmV4X3Bsb3RgIGZ1bmN0aW9uIGZyb20gYWZleCB0byBjcmVhdGUgYmVhdXRpZnVsIHBsb3RzLiBUaG9zZSBwbG90cyBpbnRlcmFjdHMgbmljZWx5IHdpdGggZ2dwbG90OgpgYGB7ciBtZXNzYWdlPUYsIHdhcm5pbmc9RiwgZHBpPSAzMDB9CmFmZXhfcGxvdChhb3ZfbTEsIHggPSAic3RhZ2UiLCB0cmFjZSA9ICJncm91cCIsIGVycm9yPSdiZXR3ZWVuJywKICAgICAgICAgIGxpbmVfYXJnID0gbGlzdChzaXplPTEpLAogICAgICAgICAgcG9pbnRfYXJnID0gbGlzdChzaXplPTMuNSksCiAgICAgICAgICBkYXRhX2FyZyA9IGxpc3Qoc2l6ZT0gMSwgY29sb3I9ICdncmV5Jywgd2lkdGg9LjQpLAogICAgICAgICAgZGF0YV9nZW9tID0gZ2VvbV9ib3hwbG90LAogICAgICAgICAgbWFwcGluZyA9IGMoImxpbmV0eXBlIiwgInNoYXBlIiwgImZpbGwiKSwKICAgICAgICAgIGxlZ2VuZF90aXRsZSA9ICJHcm91cCIpICsKICBsYWJzKHkgPSAiRGVwcmVzc2lvbiBTY29yZSIsIHggPSAiIikgKwogIHRoZW1lX2J3KCkrICMgcmVtb3ZlIHRoZSBncmV5IGJhY2tncm91bmQgYW5kIGdyaWQKICB0aGVtZShheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTMpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzKSwKICAgICAgICBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMyksCiAgICAgICAgbGVnZW5kLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEzKSwKICAgICAgICBsZWdlbmQucG9zaXRpb249J2JvdHRvbScsCiAgICAgICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgxLCAiY20iKSwKICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAnYmxhY2snLCBmaWxsID0gJ3doaXRlJywgbGluZXR5cGU9J3NvbGlkJykpKwogIHNjYWxlX2NvbG9yX3NpbXBzb25zKCkgKwogIHNjYWxlX2ZpbGxfc2ltcHNvbnMoKQpgYGAKCgpJZiB5b3UgYXJlIGludGVyZXN0ZWQgaW4gdGhpcyB0b3BpYywgY2hlY2sgb3V0IHRoaXMgbmljZSB0dXRvcmlhbCBhYm91dCBbdXNpbmcgYWZleCB0byBydW4gQU5PVkFdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9hZmV4L3ZpZ25ldHRlcy9hZmV4X2Fub3ZhX2V4YW1wbGUuaHRtbCksIGFuZCBhbHNvIHRoaXMgaW50ZXJlc3RpbmcgdHV0b3JpYWwgb24gdGhlIFtlbW1lYW5zIHBhY2thZ2VdKGh0dHBzOi8vYW9zbWl0aC5yYmluZC5pby8yMDE5LzAzLzI1L2dldHRpbmctc3RhcnRlZC13aXRoLWVtbWVhbnMvKS4KCiogKkV4ZXJjaXNlKjogUm90ZWxsbyBldCBhbC4gKDIwMTgpIGludmVzdGlnYXRlZCB0aGUgYXNzb2NpYXRpb24gYmV0d2VlbiB0aGUgcmFjZSAoV2hpdGUgdnMuIEJsYWNrIGZhY2VzKSBhbmQgdGhlIGd1bi10b29sIGp1ZGdtZW50cy4gSW4gdGhlaXIgZmlyc3QgZXhwZXJpbWVudHMsIHRoZXkgcHJlc2VudGVkIHBhcnRpY2lwYW50cyB3aXRoIDE2IFdoaXRlIG1hbGUgZmFjZXMgYW5kIDE2IEJsYWNrIG1hbGUgZmFjZXMsIGFuZCBmb2xsb3dpbmcgdGhhdCA4IGltYWdlcyBvZiBndW5zIGFuZCA4IGltYWdlcyBvZiB0b29scy4gVGhleSBhc2tlZCBwYXJ0aWNpcGFudHMgdG8ganVkZ2UgaWYgdGhlIG9iamVjdCBpcyBhIHRvb2wgb3IgYSBndW4gYnkgcHJlc3Npbmcga2V5Ym9hcmQgYnV0dG9ucy4gVGhlbiwgdGhleSByYW4gYW4gQU5PVkEgdG8gc2VlIGlmIHBhcnRpY2lwYW50cycgZ3VuIHJlc3BvbnNlcyBhcmUgaGlnaGVyIGZvciBhbnkgb2YgdGhlIHJhY2VzLiBTbywgdGhleSBpbmNsdWRlZCBwcmltZSByYWNlIChCbGFjaywgV2hpdGUpIGFuZCB0YXJnZXQgaWRlbnRpdHkgKGd1biwgdG9vbCkgYXMgaW5kZXBlbmRlbnQgdmFyaWFibGVzIGFuZCBwYXJ0aWNpcGFudHMnIGd1biByZXNwb25zZXMgYXMgZGVwZW5kZW50IHZhcmlhYmxlIGludG8gdGhlaXIgbGluZWFyIG1vZGVsIChTZWUgdGhlIG9yaWdpbmFsIHN0dWR5IFtoZXJlXShodHRwczovL29ubGluZS51Y3ByZXNzLmVkdS9jb2xsYWJyYS9hcnRpY2xlLzQvMS8zMi8xMTI5ODYvVGhlLVNoYXBlLW9mLVJPQy1DdXJ2ZXMtaW4tU2hvb3Rlci1UYXNrcykpLiBUaGV5IGZvdW5kIHRoYXQ6IAoKPiAiUGFydGljaXBhbnRzIG1hZGUgbW9yZSBndW4gcmVzcG9uc2VzIHRvIGd1bnMgdGhhbiB0byB0b29scywgRigxLDQ1KSA9IDUzMjQzLCBwIDwgMC4wMDAxLCDOtzJnID0gMC45OTguIEhvd2V2ZXIsIHRoZSByYWNlIG9mIHRoZSBwcmltZSBmYWNlIGRpZCBub3QgbWF0dGVyLCBGKDEsNDUpID0gMC4yODcsIHAgPiAwLjU5LCDOtzJnID0gMC4wMDEsIG5vciB3YXMgdGhlcmUgYW4gaW50ZXJhY3Rpb24gb2YgcHJpbWUgcmFjZSB3aXRoIHRhcmdldCBvYmplY3QsIEYoMSw0NSkgPSAwLjAyMiwgcCA+IDAuODgsIM63MmcgPSAwLjAwMCkiLgoKT3BlbiB0aGUgYHJvdGVsbG9fc2hvb3Rlcl9leHAxLmNzdmAgZmlsZSBhbmQgdHJ5IHRvIHJlcHJvZHVjZSB0aGVpciByZXN1bHRzLiBSdW4gYW4gQU5PVkEgKHR5cGUgSUlJKSB3aXRoIGByZXNwYCBhcyB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGFuZCB0YXJnZXQsIHByaW1lLCBhbmQgdGhlaXIgaW50ZXJhY3Rpb24gYXMgaW5kZXBlbmRlbnQgdmFyaWFibGVzLgoKCmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQojIGxvYWQgdGhlIGdlbmVyYWwgZGF0YSBmaWxlCnJvdGVsbG9fZGF0YSA8LSByZWFkX2NzdihoZXJlKCJjbGVhbmVkX2RhdGEiLCJyb3RlbGxvX3Nob290ZXJfZXhwMS5jc3YiKSkKCiMgQU5PVkEKcm90ZWxsb19hb3YgPC0gYW92X2NhciAocmVzcCB+IHRhcmdldCpwcmltZSArCiAgICAgICAgICAgRXJyb3Ioc3ViamVjdC90YXJnZXQqcHJpbWUpLCBkYXRhID0gcm90ZWxsb19kYXRhKQpgYGAKCmBgYHtyIGVjaG89Rn0Ka25pdHI6OmthYmxlKG5pY2Uocm90ZWxsb19hb3YpKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJib3JkZXJlZCIsICJjb25kZW5zZWQiKSwgZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gVCkKYGBgCgoKCiMjIENvcnJlbGF0aW9uCgpIZXJlLCB3ZSB3YW50IHRvIGNoZWNrIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHZhcmlhYmxlcyBvbiB0aGUgYG5hcmNpc3Npc21fZGF0YWAuIEZpcnN0LCB3ZSBuZWVkIHRvIHJlbW92ZSBgc3ViamVjdGAgY29sdW1uIGJlY2F1c2UgaXQgaXMgbm90IG51bWVyaWM6CmBgYHtyIG1lc3NhZ2U9Riwgd2FybmluZz1GfQpuYXJjaXNzaXNtX2RhdGFfY29yIDwtIG5hcmNpc3Npc21fZGF0YSAlPiUKICBzZWxlY3QoLXN1YmplY3QpCmBgYAoKYGBge3IgbWVzc2FnZT1GLCBldmFsPUYsIGZpZy5hbGlnbj0nY2VudGVyJywgZHBpPTMwMH0KCiMtLSBCYXNlIFI6CmNvcihuYXJjaXNzaXNtX2RhdGFfY29yLCBtZXRob2QgPSAicGVhcnNvbiIsICB1c2UgPSAiY29tcGxldGUub2JzIikKCiMtLSBQc3ljaCBsaWJyYXJ5Ogpwc3ljaDo6cGFpcnMucGFuZWxzKG5hcmNpc3Npc21fZGF0YV9jb3IsIG1ldGhvZCA9ICJwZWFyc29uIiwgaGlzdC5jb2wgPSAiIzAwQUZCQiIsIGRlbnNpdHkgPSBULCBlbGxpcHNlcyA9IEYsIHN0YXJzID0gVCkKCiMtLSBDb3JyZWxhdGlvbiBsaWJyYXJ5OgojIGluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImVhc3lzdGF0cy9jb3JyZWxhdGlvbiIpCiNsaWJyYXJ5KCJjb3JyZWxhdGlvbiIpCmNvcnJlbGF0aW9uOjpjb3JyZWxhdGlvbihuYXJjaXNzaXNtX2RhdGFfY29yKSAlPiUgc3VtbWFyeSgpCgojLS0gYXBhVGFibGVzIGxpYnJhcnk6Cm5hcmNpc3Npc21fZGF0YV9jb3IgJT4lIAogIGFwYVRhYmxlczo6YXBhLmNvci50YWJsZShmaWxlbmFtZT0iLi9vdXRwdXRzL0Nvck1hdHJpeC5kb2MiLCBzaG93LmNvbmYuaW50ZXJ2YWw9VCkKYGBgCgpgYGB7ciBtZXNzYWdlPUYsIGVjaG89RiwgZmlnLmFsaWduPSdjZW50ZXInLCBkcGk9MzAwfQoKIy0tIEJhc2UgUjoKY29yKG5hcmNpc3Npc21fZGF0YV9jb3IsIG1ldGhvZCA9ICJwZWFyc29uIiwgIHVzZSA9ICJjb21wbGV0ZS5vYnMiKSU+JQogIGtuaXRyOjprYWJsZShkaWdpdHMgPSAyKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJib3JkZXJlZCIsICJjb25kZW5zZWQiKSwgZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gRikKCiMtLSBQc3ljaCBsaWJyYXJ5Ogpwc3ljaDo6cGFpcnMucGFuZWxzKG5hcmNpc3Npc21fZGF0YV9jb3IsIG1ldGhvZCA9ICJwZWFyc29uIiwgaGlzdC5jb2wgPSAiIzAwQUZCQiIsIGRlbnNpdHkgPSBULCBlbGxpcHNlcyA9IEYsIHN0YXJzID0gVCkKCiMtLSBDb3JyZWxhdGlvbiBsaWJyYXJ5OgojIGluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImVhc3lzdGF0cy9jb3JyZWxhdGlvbiIpCiNsaWJyYXJ5KCJjb3JyZWxhdGlvbiIpCmNvcnJlbGF0aW9uOjpjb3JyZWxhdGlvbihuYXJjaXNzaXNtX2RhdGFfY29yKSAlPiUgc3VtbWFyeSgpJT4lCiAga25pdHI6OmthYmxlKGRpZ2l0cyA9IDIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImJvcmRlcmVkIiwgImNvbmRlbnNlZCIpLCBmaXhlZF90aGVhZCA9IFQsIGZ1bGxfd2lkdGggPSBGKQoKYGBgCgoKKiAqRXhlcmNpc2UqOiBQZW5ueWNvb2sgZXQgYWwuICgyMDIwKSBpbnZlc3RpZ2F0ZWQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGFjdGl2ZWx5IG9wZW4tbWluZGVkIHRoaW5raW5nIHN0eWxlIGFib3V0IGV2aWRlbmNlIChBT1QtRSkgYW5kIGRpZmZlcmVudCBwb2xpdGljYWwsIHNjaWVudGlmaWMsIGFuZCByZWxpZ2lvdXMgYmVsaWVmcyAoc2VlIHRoZSBvcmlnaW5hbCBwYXBlciBbaGVyZV0oaHR0cHM6Ly9wc3lhcnhpdi5jb20vYTdrOTYpKS4gSW4gdGhlaXIgZmlyc3QgZXhwZXJpbWVudCwgdGhleSBjYWxjdWxhdGVkIHRoZSBjb3JyZWxhdGlvbiBvZiBBT1RFIGFuZCBzY2llbnRpZmljIGJlbGllZnMgaXRlbXMgKGdsb2JhbCB3YXJtaW5nLCBldm9sdXRpb24sIGV0Yy4pIGFuZCB0aGV5IGZvdW5kIHRoZSBmb2xsb3dpbmcgcmVzdWx0czoKCmBgYHtyIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNzAwcHgiLCBvdXQuaGVpZ2h0PSIzNTBweCIsIGZpZy5jYXA9ICJhZGFwdGVkIGZyb20gW1Blbm55Y29vayBldCBhbC4gKDIwMjApXShodHRwczovL3BzeWFyeGl2LmNvbS9hN2s5NikifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCdpbnB1dHMnLCdwZW5ueWNvb2tfY29yci5wbmcnKSkKYGBgCgpPcGVuIHRoZSBgcGVubnljb29rX2FvdGVfZXhwMS5jc3ZgIGZpbGUgYW5kIHRyeSB0byByZXByb2R1Y2UgdGhlaXIgcmVzdWx0cyBieSBjcmVhdGluZyB0aGUgc2FtZSBjb3JyZWxhdGlvbiBtYXRyaXguCgpgYGB7ciBtZXNzYWdlPUYsIGV2YWw9Rn0KcGVubnljb29rX2RhdGEgPC0gcmVhZF9jc3YoaGVyZSgiY2xlYW5lZF9kYXRhIiwicGVubnljb29rX2FvdGVfZXhwMS5jc3YiKSkgCgoKIy0tLS0tLS0tLS0gQmFzZSBSOgpjb3IocGVubnljb29rX2RhdGEsIG1ldGhvZCA9ICJwZWFyc29uIiwgIHVzZSA9ICJjb21wbGV0ZS5vYnMiKQoKIy0tLS0tLS0tLS0gUHN5Y2ggbGlicmFyeToKcGVubnljb29rX2RhdGEgJT4lIAogIHBzeWNoOjpwYWlycy5wYW5lbHMobWV0aG9kID0gInBlYXJzb24iLCBoaXN0LmNvbCA9ICIjMDBBRkJCIiwgZGVuc2l0eSA9IFQsIGVsbGlwc2VzID0gRiwgc3RhcnMgPSBUKQoKIy0tLS0tLS0tLS0gQ29ycmVsYXRpb24gbGlicmFyeToKY29ycmVsYXRpb246OmNvcnJlbGF0aW9uKHBlbm55Y29va19kYXRhKSAlPiUgc3VtbWFyeSgpCgojLS0tLS0tLS0tLSBhcGFUYWJsZXMgbGlicmFyeToKcGVubnljb29rX2RhdGEgJT4lIAogIGFwYVRhYmxlczo6YXBhLmNvci50YWJsZShmaWxlbmFtZT0iLi9vdXRwdXRzL0Nvck1hdHJpeC5kb2MiLCBzaG93LmNvbmYuaW50ZXJ2YWw9VCkKYGBgCgoKYGBge3IgbWVzc2FnZT1GLCBldmFsPVQsIGVjaG89RiwgZmlnLmFsaWduPSdjZW50ZXInLCBkcGk9MzAwfQpwZW5ueWNvb2tfZGF0YSA8LSByZWFkX2NzdihoZXJlKCJjbGVhbmVkX2RhdGEiLCJwZW5ueWNvb2tfYW90ZV9leHAxLmNzdiIpKSAlPiUKICBjbGVhbl9uYW1lcygpCgpjb3JyZWxhdGlvbjo6Y29ycmVsYXRpb24ocGVubnljb29rX2RhdGEpICU+JSBzdW1tYXJ5KCkgJT4lCiAga25pdHI6OmthYmxlKGRpZ2l0cyA9IDIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImJvcmRlcmVkIiwgImNvbmRlbnNlZCIpLCBmaXhlZF90aGVhZCA9IFQsIGZ1bGxfd2lkdGggPSBGKSU+JQogIHNjcm9sbF9ib3god2lkdGggPSAiNzgwcHgiKQoKYGBgCgoKIyMgTGluZWFyIFJlZ3Jlc3Npb24KCkhlcmUsIHdlIGRvIHNpbmdsZSBhbmQgbXVsdGlwbGUgbGluZWFyIHJlZ3JlYXNzaW9uIG9uIHRoZSBgbmFyY2lzc2lzbV9kYXRhYDoKCmBgYHtyfQptMSA8LSBsbShtZW50YWxfaGVhbHRofm5hcmNpc3Npc20sIGRhdGE9IG5hcmNpc3Npc21fZGF0YSkKYGBgCgpgYGB7ciBtZXNzYWdlPUYsIGV2YWw9VCwgZWNobz1GLCBmaWcuYWxpZ249J2NlbnRlcicsIGRwaT0zMDB9CmJyb29tOjp0aWR5KG0xKSU+JQogIGtuaXRyOjprYWJsZShkaWdpdHMgPSAyKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJib3JkZXJlZCIsICJjb25kZW5zZWQiKSwgZml4ZWRfdGhlYWQgPSBULCBmdWxsX3dpZHRoID0gVCkKYGBgCgpgYGB7cn0KbTIgPC0gbG0obWVudGFsX2hlYWx0aH5uYXJjaXNzaXNtK3BzeWNob3BhdGh5LCBkYXRhPSBuYXJjaXNzaXNtX2RhdGEpCmBgYAoKYGBge3IgbWVzc2FnZT1GLCBldmFsPVQsIGVjaG89RiwgZmlnLmFsaWduPSdjZW50ZXInLCBkcGk9MzAwfQpicm9vbTo6dGlkeShtMiklPiUKICBrbml0cjo6a2FibGUoZGlnaXRzID0gMikgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiYm9yZGVyZWQiLCAiY29uZGVuc2VkIiksIGZpeGVkX3RoZWFkID0gVCwgZnVsbF93aWR0aCA9IFQpCmBgYAoKKiAqRXhlcmNpc2UqOiBUcsOpbW9sacOocmUgYW5kIERqZXJpb3VhdCAoMjAyMCkgZXhhbWluZWQgdGhlIHJvbGUgb2YgKmNvZ25pdGl2ZSByZWZsZWN0aW9uKiBhbmQgKmJlbGllZiBpbiBzY2llbmNlKiBpbiBjbGltYXRlIGNoYW5nZSBza2VwdGljaXNtLiBJbiB0aGVpciBmaXJzdCBzdHVkeSwgdGhleSByZXZlYWxlZCB0aGF0IGNvZ25pdGl2ZSByZWZsZWN0aW9uIGFuZCBiZWxpZWYgaW4gc2NpZW5jZSBuZWdldGl2ZWx5IHByZWRpY3RlZCBjbGltYXRlIGNoYW5nZSBza2VwdGljaXNtIGV2ZW4gYWZ0ZXIgY29udHJvbGxpbmcgZm9yIGRlbW9ncmFwaGljIGFuZCBjb2duaXRpdmUgYWJpbGl0eSB2YXJpYWJsZXMgKHNlZSB0aGUgb3JpZ2luYWwgcGFwZXIgW2hlcmVdKGh0dHBzOi8vcHN5YXJ4aXYuY29tL3ZwOGs2LykpLiAKCmBgYHtyIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNzAwcHgiLCBvdXQuaGVpZ2h0PSIzNTBweCIsIGZpZy5jYXA9ICJhZGFwdGVkIGZyb20gW1Ryw6ltb2xpw6hyZSBhbmQgRGplcmlvdWF0ICgyMDIwKV0oaHR0cHM6Ly9wc3lhcnhpdi5jb20vdnA4azYvKSJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmUoJ2lucHV0cycsJ3RyZW1vbGllcmVfcmVnLnBuZycpKQpgYGAKCk9wZW4gdGhlIGB0cmVtb2xpZXJlX2RhdGFfZXhwMS5jc3ZgIGZpbGUgYW5kIHRyeSB0byByZXByb2R1Y2UgdGhlaXIgcmVzdWx0cyBieSBydW5uaW5nIGEgbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24uIEVudGVyIGFnZSwgZ2VuZGVyLCBlZHVjYXRpb24sIGJlbGllZiBpbiBzY2llbmNlLCBsaXRlcmFjeSwgbnVtZXJhY3kgKE51bXRvdGFsKSwgYW5kIGNvZ25pdGl2ZSByZWZsZWN0aW9uIGFzIHByZWRpY3RvcnMgYW5kIGVudGVyIGNsaW1hdGUgY2hhbmdlIHNrZXB0aWNpc20gKGNsaW1hdG8pIGFzIHRoZSBvdXRjb21lIHZhcmlhYmxlLgoKYGBge3IgbWVzc2FnZT1GfQpUcmVtb2xpZXJlX2RhdGEgPC0gcmVhZF9jc3YoaGVyZSgiY2xlYW5lZF9kYXRhIiwidHJlbW9saWVyZV9kYXRhX2V4cDEuY3N2IikpCgpUcmVtb2xpZXJlX3JlZz1sbShDbGltYXRvIH4gQWdlKyBHZW5kZXIrIEVkdWNhdGlvbisgQmVsaWVmSW5TY2llbmNldG90YWwrIExpdGVyYWN5KyBOdW10b3RhbCsgQ29nbml0aXZlUmVmbGVjdGlvbiwKICAgICAgICAgICAgICAgICAgICBkYXRhPVRyZW1vbGllcmVfZGF0YSkKYGBgCgoKYGBge3IgbWVzc2FnZT1GLCBldmFsPVQsIGVjaG89RiwgZmlnLmFsaWduPSdjZW50ZXInLCBkcGk9MzAwfQpicm9vbTo6dGlkeShUcmVtb2xpZXJlX3JlZyklPiUKICBrbml0cjo6a2FibGUoZGlnaXRzID0gMikgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiYm9yZGVyZWQiLCAiY29uZGVuc2VkIiksIGZpeGVkX3RoZWFkID0gVCwgZnVsbF93aWR0aCA9IFQpCgpnbGFuY2UoVHJlbW9saWVyZV9yZWcpJT4lCiAga25pdHI6OmthYmxlKGRpZ2l0cyA9IDIpICU+JQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImJvcmRlcmVkIiwgImNvbmRlbnNlZCIpLCBmaXhlZF90aGVhZCA9IFQsIGZ1bGxfd2lkdGggPSBGKSU+JQogIHNjcm9sbF9ib3god2lkdGggPSAiNzgwcHgiKQpgYGAKCgojIFJtYXJrZG93bgoKVG8gYmUgY29tcGxldGVkLi4uCgoKYGBge3IgZWNobz1GQUxTRSwgb3V0LndpZHRoPSI3MDBweCIsIG91dC5oZWlnaHQ9IjM1MHB4IiwgZmlnLmNhcD0gIkFydHdvcmsgYnkgQWxsaXNvbiBIb3JzdDogaHR0cHM6Ly9naXRodWIuY29tL2FsbGlzb25ob3JzdC9zdGF0cy1pbGx1c3RyYXRpb25zIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZSgnaW5wdXRzJywncm1hcmtkb3duX3dpemFyZHMucG5nJykpCmBgYAoKCmBgYHtyIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNzAwcHgiLCBvdXQuaGVpZ2h0PSIzNTBweCIsIGZpZy5jYXA9ICJBcnR3b3JrIGJ5IEFsbGlzb24gSG9yc3Q6IGh0dHBzOi8vZ2l0aHViLmNvbS9hbGxpc29uaG9yc3Qvc3RhdHMtaWxsdXN0cmF0aW9ucyJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmUoJ2lucHV0cycsJ3JlcHJvZHVjaWJpbGl0eV9jb3VydC5wbmcnKSkKYGBgCgojIFJlZmVyZW5jZXMKCi0gR2hhc2VtaSwgTy4sIEhhbmRsZXksIFMuLCAmIEhvd2FydGgsIFMuICgyMDIwKS4gVGhlIEJyaWdodCBIb211bmN1bHVzIGluIG91ciBIZWFkOiBJbmRpdmlkdWFsIERpZmZlcmVuY2VzIGluIEludHVpdGl2ZSBTZW5zaXRpdml0eSB0byBMb2dpY2FsIFZhbGlkaXR5LgoKLSBKb2huLCBMLiBLLiwgSmVvbmcsIE0uLCBHaW5vLCBGLiwgJiBIdWFuZywgTC4gKDIwMTkpLiBUaGUgc2VsZi1wcmVzZW50YXRpb25hbCBjb25zZXF1ZW5jZXMgb2YgdXBob2xkaW5nIG9uZeKAmXMgc3RhbmNlIGluIHNwaXRlIG9mIHRoZSBldmlkZW5jZS4gT3JnYW5pemF0aW9uYWwgQmVoYXZpb3IgYW5kIEh1bWFuIERlY2lzaW9uIFByb2Nlc3NlcywgMTU0LCAxLTE0LgoKLSBQZW5ueWNvb2ssIEcuLCBDaGV5bmUsIEouIEEuLCBLb2VobGVyLCBELiBKLiwgJiBGdWdlbHNhbmcsIEouIEEuICgyMDIwKS4gT24gdGhlIGJlbGllZiB0aGF0IGJlbGllZnMgc2hvdWxkIGNoYW5nZSBhY2NvcmRpbmcgdG8gZXZpZGVuY2U6IEltcGxpY2F0aW9ucyBmb3IgY29uc3BpcmF0b3JpYWwsIG1vcmFsLCBwYXJhbm9ybWFsLCBwb2xpdGljYWwsIHJlbGlnaW91cywgYW5kIHNjaWVuY2UgYmVsaWVmcy4gSnVkZ21lbnQgYW5kIERlY2lzaW9uIE1ha2luZywgMTUoNCksIDQ3Ni4KCi0gUm90ZWxsbywgQy4gTS4sIEtlbGx5LCBMLiBKLiwgSGVpdCwgRS4sIFZhemlyZSwgUy4sICYgVnVsLCBFLiAoMjAxOCkuIFRoZSBTaGFwZSBvZiBST0MgQ3VydmVzIGluIFNob290ZXIgVGFza3M6IEltcGxpY2F0aW9ucyBmb3IgQmVzdCBQcmFjdGljZXMgaW4gQW5hbHlzaXMuIENvbGxhYnJhOiBQc3ljaG9sb2d5LCA0KDEpLgoKLSBUcsOpbW9sacOocmUsIEIuLCAmIERqZXJpb3VhdCwgSC4gKDIwMjApLiBEb27igJl0IHlvdSBzZWUgdGhhdCBpdHMgY29sZCEgRXhwbG9yaW5nIHRoZSByb2xlcyBvZiBjb2duaXRpdmUgcmVmbGVjdGlvbiwgY2xpbWF0ZSBzY2llbmNlIGxpdGVyYWN5LCBpbGx1c2lvbiBvZiBrbm93bGVkZ2UsIGFuZCBwb2xpdGljYWwgb3JpZW50YXRpb24gaW4gY2xpbWF0ZSBjaGFuZ2Ugc2tlcHRpY2lzbS4KCi0gV2lja2hhbSwgSC4gKDIwMTQpLiBUaWR5IGRhdGEuIEpvdXJuYWwgb2YgU3RhdGlzdGljYWwgU29mdHdhcmUsIDU5KDEwKSwgMS0yMy4=